<!DOCTYPE html>
<html lang="zh" dir="ltr">

<head prefix="og: http://ogp.me/ns#">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>linux内存物理内存分配伙伴系统 – Hugo Zzo Theme</title>
    


<script src="/js/enquire.min.dfb99dee1e029d51d6cfb672d847929890b1585402de17f5ed092edd72a688b4.js"></script>

<script defer src="/js/lazysizes.min.31dd6a2d3a1ec0f78a8df007535cf23f03aeb5c70f026e6d6a19dac3b3acc340.js"></script>

<script defer src="/js/fuse.min.94c78ad70b02749822921660cf4e9f0b3701bc0680c421afb784a78228de0275.js"></script>

<script defer src="/js/helper/getParents.min.1618c696be7c98933f9a92677f518b512a74e55bdbb976b09936b4182e93181b.js"></script>

<script defer src="/js/helper/fadeinout.min.93a331f96194789a542f33690bbe4f0c102c7e78ffc018217f5a1c33010bad91.js"></script>

<script defer src="/js/helper/closest.min.js"></script>
  
<script>
  "use strict";

  
  
  if (window.NodeList && !NodeList.prototype.forEach) {
    NodeList.prototype.forEach = Array.prototype.forEach;
  }

  
  if (!String.prototype.includes) {
    String.prototype.includes = function (search, start) {
      'use strict';

      if (search instanceof RegExp) {
        throw TypeError('first argument must not be a RegExp');
      }
      if (start === undefined) { start = 0; }
      return this.indexOf(search, start) !== -1;
    };
  }

  
  Document.prototype.append = Element.prototype.append = function append() {
    this.appendChild(_mutation(arguments));
  };
  function _mutation(nodes) {
    if (!nodes.length) {
      throw new Error('DOM Exception 8');
    } else if (nodes.length === 1) {
      return typeof nodes[0] === 'string' ? document.createTextNode(nodes[0]) : nodes[0];
    } else {
      var
      fragment = document.createDocumentFragment(),
      length = nodes.length,
      index = -1,
      node;

      while (++index < length) {
        node = nodes[index];

        fragment.appendChild(typeof node === 'string' ? document.createTextNode(node) : node);
      }

      return fragment;
    }
  }

  
  if (!String.prototype.startsWith) {
    String.prototype.startsWith = function (searchString, position) {
      position = position || 0;
      return this.indexOf(searchString, position) === position;
    };
  }
  


  document.addEventListener('DOMContentLoaded', function () {
    
    var navCollapseBtn = document.querySelector('.navbar__burger');
    navCollapseBtn ? navCollapseBtn.addEventListener('click', function (e) {
      var navCollapse = document.querySelector('.navbarm__collapse');

      if (navCollapse) {
        var dataOpen = navCollapse.getAttribute('data-open');

        if (dataOpen === 'true') {
          navCollapse.setAttribute('data-open', 'false');
          navCollapse.style.maxHeight = 0;
          navCollapseBtn.classList.remove('is-active');
        } else {
          navCollapse.setAttribute('data-open', 'true');
          navCollapse.style.maxHeight = navCollapse.scrollHeight + "px";
          navCollapseBtn.classList.add('is-active');
        }
      }
    }) : null;
    


    
    var summaryContainer = document.querySelector('.summary__container');
    var searchResult = document.querySelector('.search-result');
    var searchResultCloseBtn = document.querySelector('.search-result__close');
    searchResultCloseBtn ? searchResultCloseBtn.addEventListener('click', function (e) {
      searchResult.setAttribute('data-display', 'none');
      summaryContainer.setAttribute('data-display', 'block');
    }) : null;
    


    
    document.querySelectorAll('.tab') ? 
    document.querySelectorAll('.tab').forEach(function(elem, idx) {
      var containerId = elem.getAttribute('id');
      var containerElem = elem;
      var tabLinks = elem.querySelectorAll('.tab__link');
      var tabContents = elem.querySelectorAll('.tab__content');
      var ids = [];

      tabLinks && tabLinks.length > 0 ?
      tabLinks.forEach(function(link, index, self) {
        link.onclick = function(e) {
          for (var i = 0; i < self.length; i++) {
            if (index === parseInt(i, 10)) {
              if (!self[i].classList.contains('active')) {
                self[i].classList.add('active');
                tabContents[i].style.display = 'block';
              }
            } else {
              self[i].classList.remove('active');
              tabContents[i].style.display = 'none';
            }
          }
        }
      }) : null;
    }) : null;
    


    
    document.querySelectorAll('.codetab') ? 
    document.querySelectorAll('.codetab').forEach(function(elem, idx) {
      var containerId = elem.getAttribute('id');
      var containerElem = elem;
      var codetabLinks = elem.querySelectorAll('.codetab__link');
      var codetabContents = elem.querySelectorAll('.codetab__content');
      var ids = [];

      codetabLinks && codetabLinks.length > 0 ?
      codetabLinks.forEach(function(link, index, self) {
        link.onclick = function(e) {
          for (var i = 0; i < self.length; i++) {
            if (index === parseInt(i, 10)) {
              if (!self[i].classList.contains('active')) {
                self[i].classList.add('active');
                codetabContents[i].style.display = 'block';
              }
            } else {
              self[i].classList.remove('active');
              codetabContents[i].style.display = 'none';
            }
          }
        }
      }) : null;
    }) : null;
    


    
    var gttBtn = document.getElementById("gtt");
    gttBtn.style.display = "none";
    gttBtn.addEventListener('click', function () {
      if (window.document.documentMode) {
        document.documentElement.scrollTop = 0;
      } else {
        scrollToTop(250);
      }
    });

    function scrollToTop(scrollDuration) {
      var scrollStep = -window.scrollY / (scrollDuration / 15);
      var scrollInterval = setInterval(function () {
        if (window.scrollY != 0) {
          window.scrollBy(0, scrollStep);
        }
        else clearInterval(scrollInterval);
      }, 15);
    }

    var scrollFunction = function () {
      if (document.body.scrollTop > 250 || document.documentElement.scrollTop > 250) {
        gttBtn.style.display = "block";
      } else {
        gttBtn.style.display = "none";
      }
    }
    


    
    var expandBtn = document.querySelectorAll('.expand__button');

    for (let i = 0; i < expandBtn.length; i++) {
      expandBtn[i].addEventListener("click", function () {
        var content = this.nextElementSibling;
        if (content.style.maxHeight) {
          content.style.maxHeight = null;
          this.querySelector('svg').classList.add('expand-icon__right');
          this.querySelector('svg').classList.remove('expand-icon__down');
        } else {
          content.style.maxHeight = content.scrollHeight + "px";
          this.querySelector('svg').classList.remove('expand-icon__right');
          this.querySelector('svg').classList.add('expand-icon__down');
        }
      });
    }
    


    
    var lastScrollTop = window.pageYOffset || document.documentElement.scrollTop;
    var tocElem = document.querySelector('.toc');
    var tableOfContentsElem = tocElem ? tocElem.querySelector('#TableOfContents') : null;
    var toggleTocElem = document.getElementById('toggle-toc');
    var singleContentsElem = document.querySelector('.single__contents');
    var navbar = document.querySelector('.navbar');
    var tocFlexbox = document.querySelector('.toc__flexbox');
    var tocFlexboxOuter = document.querySelector('.toc__flexbox--outer');
    var expandContents = document.querySelectorAll('.expand__content');
    var boxContents = document.querySelectorAll('.box');
    var notAllowedTitleIds = null;

    
    var tocFolding = JSON.parse("null");
    
    var tocLevels = JSON.parse("null");
    
    if (tocLevels) {
      tocLevels = tocLevels.toString();
    } else {
      tocLevels = "h1, h2, h3, h4, h5, h6";
    }

    
    singleContentsElem && singleContentsElem.querySelectorAll(".tab") ?
    singleContentsElem.querySelectorAll(".tab").forEach(function (elem) {
      elem.querySelectorAll(tocLevels).forEach(function (element) {
        notAllowedTitleIds = Array.isArray(notAllowedTitleIds) ?
          notAllowedTitleIds.concat(element.getAttribute('id')) :
          [element.getAttribute('id')];
      });
    }) : null;

    
    expandContents ? expandContents.forEach(function(elem) {
      elem.querySelectorAll(tocLevels).forEach(function (element) {
        notAllowedTitleIds = Array.isArray(notAllowedTitleIds) ?
          notAllowedTitleIds.concat(element.getAttribute('id')) :
          [element.getAttribute('id')];
      });
    }) : null;

    
    boxContents ? boxContents.forEach(function(elem) {
      elem.querySelectorAll(tocLevels).forEach(function (element) {
        notAllowedTitleIds = Array.isArray(notAllowedTitleIds) ?
          notAllowedTitleIds.concat(element.getAttribute('id')) :
          [element.getAttribute('id')];
      });
    }) : null;

    
    window.onscroll = function () {
      scrollFunction();
      
      var st = window.pageYOffset || document.documentElement.scrollTop;
      if (st > lastScrollTop) { 
        if (st < 250) {
          gttBtn.style.display = "none";
        } else {
          gttBtn.style.display = "block";
        }

        if (st < 45) {
          return null;
        }
        
        if (!navbar.classList.contains('navbar--hide')) {
          navbar.classList.add('navbar--hide');
        } else if (navbar.classList.contains('navbar--show')) {
          navbar.classList.remove('navbar--show');
        }

        if (singleContentsElem) {
          if (singleContentsElem.querySelectorAll(tocLevels).length > 0) {
            singleContentsElem.querySelectorAll(tocLevels).forEach(function (elem) {
              if (toggleTocElem && !toggleTocElem.checked) {
                return null;
              }

              if (notAllowedTitleIds && notAllowedTitleIds.includes(elem.getAttribute('id'))) {
                return null;
              }
              
              if (document.documentElement.scrollTop >= elem.offsetTop) {
                if (tableOfContentsElem) {
                  var id = elem.getAttribute('id');
                  tocElem.querySelectorAll('a').forEach(function (elem) {
                    elem.classList.remove('active');
                  });
                  tocElem.querySelector('a[href="#' + id + '"]') ?
                    tocElem.querySelector('a[href="#' + id + '"]').classList.add('active') : null;

                  if (false === tocFolding) {
                    
                  } else {
                    tableOfContentsElem.querySelectorAll('ul') ?
                      tableOfContentsElem.querySelectorAll('ul').forEach(function (rootUl) {
                        rootUl.querySelectorAll('li').forEach(function (liElem) {
                          liElem.querySelectorAll('ul').forEach(function (ulElem) {
                            ulElem.style.display = 'none';
                          });
                        });
                      }) : null;
                  }

                  var curElem = tableOfContentsElem.querySelector("[href='#" + id + "']");
                  if (curElem && curElem.nextElementSibling) {
                    curElem.nextElementSibling.style.display = 'block';
                  }
                  getParents(curElem, 'ul') ?
                    getParents(curElem, 'ul').forEach(function (elem) {
                      elem.style.display = 'block';
                    }) : null;
                }
              }
            });
          } else {
            if (tocFlexbox) {
              tocFlexbox.setAttribute('data-position', '');
              if (!tocFlexbox.classList.contains('hide')) {
                tocFlexbox.classList.add('hide');
              }
            }
            if (tocFlexboxOuter) {
              tocFlexboxOuter.setAttribute('data-position', '');
              if (!tocFlexboxOuter.classList.contains('hide')) {
                tocFlexboxOuter.classList.add('hide');
              }
            }
          }
        }
      } else { 
        if (st < 250) {
          gttBtn.style.display = "none";
        }

        if (navbar.classList.contains('navbar--hide')) {
          navbar.classList.remove('navbar--hide');
        } else if (!navbar.classList.contains('navbar--show')) {
          navbar.classList.add('navbar--show');
        }

        if (singleContentsElem) {
          if (singleContentsElem.querySelectorAll(tocLevels).length > 0) {
            singleContentsElem.querySelectorAll(tocLevels).forEach(function (elem) {
              if (toggleTocElem && !toggleTocElem.checked) {
                return null;
              }
              
              if (notAllowedTitleIds && notAllowedTitleIds.includes(elem.getAttribute('id'))) {
                return null;
              }

              if (document.documentElement.scrollTop >= elem.offsetTop) {
                if (tableOfContentsElem) {
                  var id = elem.getAttribute('id');
                  tocElem.querySelectorAll('a').forEach(function (elem) {
                    elem.classList.remove('active');
                  });
                  tocElem.querySelector('a[href="#' + id + '"]') ?
                    tocElem.querySelector('a[href="#' + id + '"]').classList.add('active') : null;

                  if (false === tocFolding) {
                    
                  } else {
                    tableOfContentsElem.querySelectorAll('ul') ?
                      tableOfContentsElem.querySelectorAll('ul').forEach(function (rootUl) {
                        rootUl.querySelectorAll('li').forEach(function (liElem) {
                          liElem.querySelectorAll('ul').forEach(function (ulElem) {
                            ulElem.style.display = 'none';
                          });
                        });
                      }) : null;
                  }

                  var curElem = tableOfContentsElem.querySelector("[href='#" + id + "']");
                  if (curElem && curElem.nextElementSibling) {
                    curElem.nextElementSibling.style.display = 'block';
                  }
                  getParents(curElem, 'ul') ?
                    getParents(curElem, 'ul').forEach(function (elem) {
                      elem.style.display = 'block';
                    }) : null;
                }
              }
            });
          } else {
            if (tocFlexbox && !tocFlexbox.classList.contains('hide')) {
              tocFlexbox.classList.add('hide');
            }
            if (tocFlexboxOuter && !tocFlexboxOuter.classList.contains('hide')) {
              tocFlexboxOuter.classList.add('hide');
            }
          }
          
        }

        if (tableOfContentsElem && document.documentElement.scrollTop < 250) {
          if (false === tocFolding) {

          } else {
            tableOfContentsElem.querySelector('ul') ?
              tableOfContentsElem.querySelector('ul').querySelectorAll('li').forEach(function (liElem) {
                liElem.querySelectorAll('ul').forEach(function (ulElem) {
                  ulElem.style.display = 'none';
                });
              }) : null;
          }
        }
      }
      lastScrollTop = st <= 0 ? 0 : st;
    };
  


  
    var localTheme = localStorage.getItem('theme');
    var rootEleme = document.getElementById('root');
    var selectThemeElem = document.querySelectorAll('.select-theme');
    var selectThemeItemElem = document.querySelectorAll('.select-theme__item');

    var setMetaColor = function(themeColor) {
      var metaMsapplicationTileColor = document.getElementsByName('msapplication-TileColor')[0];
      var metaThemeColor = document.getElementsByName('theme-color')[0];
      var metaMsapplicationNavbuttonColor = document.getElementsByName('msapplication-navbutton-color')[0];
      var metaAppleMobileWebAappStatusBarStyle = document.getElementsByName('apple-mobile-web-app-status-bar-style')[0];

      if (themeColor.includes('dark')) {
        metaMsapplicationTileColor.setAttribute('content', '#fcfcfa');
        metaThemeColor.setAttribute('content', '#403E41');
        metaMsapplicationNavbuttonColor.setAttribute('content', '#403E41');
        metaAppleMobileWebAappStatusBarStyle.setAttribute('content', '#403E41');
      } else if (themeColor.includes('light')) {
        metaMsapplicationTileColor.setAttribute('content', '#555');
        metaThemeColor.setAttribute('content', '#eee');
        metaMsapplicationNavbuttonColor.setAttribute('content', '#eee');
        metaAppleMobileWebAappStatusBarStyle.setAttribute('content', '#eee');
      } else if (themeColor.includes('hacker')) {
        metaMsapplicationTileColor.setAttribute('content', '#e3cd26');
        metaThemeColor.setAttribute('content', '#252526');
        metaMsapplicationNavbuttonColor.setAttribute('content', '#252526');
        metaAppleMobileWebAappStatusBarStyle.setAttribute('content', '#252526');
      } else if (themeColor.includes('solarized')) {
        metaMsapplicationTileColor.setAttribute('content', '#d3af86');
        metaThemeColor.setAttribute('content', '#51412c');
        metaMsapplicationNavbuttonColor.setAttribute('content', '#51412c');
        metaAppleMobileWebAappStatusBarStyle.setAttribute('content', '#51412c');
      } else if (themeColor.includes('kimbie')) {
        metaMsapplicationTileColor.setAttribute('content', '#586e75');
        metaThemeColor.setAttribute('content', '#eee8d5');
        metaMsapplicationNavbuttonColor.setAttribute('content', '#eee8d5');
        metaAppleMobileWebAappStatusBarStyle.setAttribute('content', '#eee8d5');
      } 
    }
    
    if (localTheme) {
      selectThemeItemElem ? 
      selectThemeItemElem.forEach(function (elem) {
        if (elem.text.trim() === localTheme) {
          elem.classList.add('is-active');
        } else {
          elem.classList.remove('is-active');
        }
      }) : null;

      setMetaColor(localTheme);
    } else {
      setMetaColor(rootEleme.className);
    }

    selectThemeItemElem ? 
    selectThemeItemElem.forEach(function (v, i) {
      v.addEventListener('click', function (e) {
        var selectedThemeVariant = e.target.text.trim();
        localStorage.setItem('theme', selectedThemeVariant);
        setMetaColor(selectedThemeVariant);

        rootEleme.removeAttribute('class');
        rootEleme.classList.add('theme__' + selectedThemeVariant);
        selectThemeElem.forEach(function(rootElem) {
          rootElem.querySelectorAll('a').forEach(function (elem) {
            if (elem.classList) {
              if (elem.text.trim() === selectedThemeVariant) {
                if (!elem.classList.contains('is-active')) {
                  elem.classList.add('is-active');
                }
              } else {
                if (elem.classList.contains('is-active')) {
                  elem.classList.remove('is-active');
                }
              }
            }
          });
        });

        if (window.mermaid) {
          if (selectedThemeVariant === "dark" || selectedThemeVariant === "hacker") {
            mermaid.initialize({ theme: 'dark' });
            location.reload();
          } else {
            mermaid.initialize({ theme: 'default' });
            location.reload();
          }
        }

        var utterances = document.getElementById('utterances');
        if (utterances) {
          utterances.querySelector('iframe').contentWindow.postMessage({
            type: 'set-theme',
            theme: selectedThemeVariant === "dark" || selectedThemeVariant === "hacker" ? 'photon-dark' : selectedThemeVariant === 'kimbie' ? 'github-dark-orange' : 'github-light',
          }, 'https://utteranc.es');
        }

        var twitterCards = document.querySelectorAll('.twitter-timeline');
        if (twitterCards) {
          window.postMessage({
            type: 'set-twitter-theme',
            theme: selectedThemeVariant === 'light' || selectedThemeVariant === 'solarized' ? 'light' : 'dark',
          });
        }
      });
    }) : null;
  


  
    
    var permalink = JSON.parse("\"http://shaddy.gitee.io/zh/linux/linuxcore/notes/linux%E5%86%85%E5%AD%98%E7%89%A9%E7%90%86%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E4%BC%99%E4%BC%B4%E7%B3%BB%E7%BB%9F/\"");
    var searchResults = null;
    var searchMenu = null;
    var searchText = null;
    
    
    var enableSearchHighlight = JSON.parse("true");
    
    var searchResultPosition = JSON.parse("\"main\"");
    
    var sectionType = JSON.parse("\"linux\"");

    var fuse = null;

    (function initFuse() {
      var xhr = new XMLHttpRequest();
      xhr.open('GET', permalink + "index.json");
      xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
      xhr.onload = function () {
        if (xhr.status === 200) {
          fuse = new Fuse(JSON.parse(xhr.response.toString('utf-8')), {
            keys: sectionType.includes('publication') ? ['title', 'abstract'] : ['title', 'description', 'content'],
            includeMatches: enableSearchHighlight,
            shouldSort: true,
            threshold: 0.4,
            location: 0,
            distance: 100,
            maxPatternLength: 32,
            minMatchCharLength: 1,
          });
          window.fuse = fuse;
        }
        else {
          console.error('[' + xhr.status + ']Error:', xhr.statusText);
        }
      };
      xhr.send();
    })();

    function makeLi(ulElem, obj) {
      var li = document.createElement('li');
      li.className = 'search-result__item';
      
      var a = document.createElement('a');
      a.innerHTML = obj.title;
      a.setAttribute('class', 'search-result__item--title');
      a.setAttribute('href', obj.permalink);

      var descDiv = document.createElement('div');
      descDiv.setAttribute('class', 'search-result__item--desc');
      if (obj.description) {
        descDiv.innerHTML = obj.description;
      } else if (obj.content) {
        descDiv.innerHTML = obj.content.substring(0, 225);
      }
      
      li.appendChild(a);
      li.appendChild(descDiv);
      ulElem.appendChild(li);
    }

    function makeHighlightLi(ulElem, obj) {
      var li = document.createElement('li');
      li.className = 'search-result__item';
      var descDiv = null;

      var a = document.createElement('a');
      a.innerHTML = obj.item.title;
      a.setAttribute('class', 'search-result__item--title');
      a.setAttribute('href', obj.item.uri);

      if (obj.matches && obj.matches.length) {
        for (var i = 0; i < obj.matches.length; i++) {
          if ('title' === obj.matches[i].key) {
            a = document.createElement('a');
            a.innerHTML = generateHighlightedText(obj.matches[i].value, obj.matches[i].indices);
            a.setAttribute('class', 'search-result__item--title');
            a.setAttribute('href', obj.item.uri);
          }
          
          if ('description' === obj.matches[i].key) {
            descDiv = document.createElement('div');
            descDiv.setAttribute('class', 'search-result__item--desc');
            descDiv.innerHTML = generateHighlightedText(obj.item.description, obj.matches[i].indices);
          } else if ('content' === obj.matches[i].key) {
            if (!descDiv) {
              descDiv = document.createElement('div');
              descDiv.setAttribute('class', 'search-result__item--desc');
              descDiv.innerHTML = generateHighlightedText(obj.item.content.substring(0, 150), obj.matches[i].indices);
            }
          } else {
            if (obj.item.description) {
              descDiv = document.createElement('div');
              descDiv.setAttribute('class', 'search-result__item--desc');
              descDiv.innerHTML = obj.item.description;
            } else {
              descDiv = document.createElement('div');
              descDiv.setAttribute('class', 'search-result__item--desc');
              descDiv.innerHTML = obj.item.content.substring(0, 150);
            }
          }
        }

        li.appendChild(a);
        if (descDiv) {
          li.appendChild(descDiv);
        }
        if (li) {
          ulElem.appendChild(li);
        }
      }
    }

    function renderSearchResultsSide(searchText, results) {
      searchResults = document.getElementById('search-results');
      searchMenu = document.getElementById('search-menu');
      searchResults.setAttribute('class', 'dropdown is-active');
      
      var ul = document.createElement('ul');
      ul.setAttribute('class', 'dropdown-content search-content');

      if (results.length) {
        results.forEach(function (result) {
          var li = document.createElement('li');
          var a = document.createElement('a');
          a.setAttribute('href', result.uri);
          a.setAttribute('class', 'dropdown-item');
          a.appendChild(li);

          var titleDiv = document.createElement('div');
          titleDiv.innerHTML = result.title;
          titleDiv.setAttribute('class', 'menu-item__title');

          var descDiv = document.createElement('div');
          descDiv.setAttribute('class', 'menu-item__desc');
          if (result.description) {
            descDiv.innerHTML = result.description;
          } else if (result.content) {
            descDiv.innerHTML = result.content.substring(0, 150);
          }

          li.appendChild(titleDiv);
          li.appendChild(descDiv);
          ul.appendChild(a);
        });
      } else {
        var li = document.createElement('li');
        li.setAttribute('class', 'dropdown-item');
        li.innerText = 'No results found';
        ul.appendChild(li);
      }

      while (searchMenu.hasChildNodes()) {
        searchMenu.removeChild(
          searchMenu.lastChild
        );
      }
      
      searchMenu.appendChild(ul);
    }

    function renderSearchHighlightResultsSide(searchText, results) {
      searchResults = document.getElementById('search-results');
      searchMenu = document.getElementById('search-menu');
      searchResults.setAttribute('class', 'dropdown is-active');

      var ul = document.createElement('ul');
      ul.setAttribute('class', 'dropdown-content search-content');

      if (results.length) {
        results.forEach(function (result) {
          var li = document.createElement('li');
          var a = document.createElement('a');
          var descDiv = null;

          a.setAttribute('href', result.item.uri);
          a.setAttribute('class', 'dropdown-item');
          a.appendChild(li);

          var titleDiv = document.createElement('div');
          titleDiv.innerHTML = result.item.title;
          titleDiv.setAttribute('class', 'menu-item__title');
          
          if (result.matches && result.matches.length) {
            for (var i = 0; i < result.matches.length; i++) {
              if ('title' === result.matches[i].key) {
                titleDiv.innerHTML = generateHighlightedText(result.matches[i].value, result.matches[i].indices);
              }

              if ('description' === result.matches[i].key) {
                descDiv = document.createElement('div');
                descDiv.setAttribute('class', 'menu-item__desc');
                descDiv.innerHTML = generateHighlightedText(result.item.description, result.matches[i].indices);
              } else if ('content' === result.matches[i].key) {
                if (!descDiv) {
                  descDiv = document.createElement('div');
                  descDiv.setAttribute('class', 'menu-item__desc');
                  descDiv.innerHTML = generateHighlightedText(result.item.content.substring(0, 150), result.matches[i].indices);
                }
              } else {
                if (result.item.description) {
                  descDiv = document.createElement('div');
                  descDiv.setAttribute('class', 'menu-item__desc');
                  descDiv.innerHTML = result.item.description;
                } else {
                  descDiv = document.createElement('div');
                  descDiv.setAttribute('class', 'menu-item__desc');
                  descDiv.innerHTML = result.item.content.substring(0, 150);
                }
              }
            }
            
            li.appendChild(titleDiv);
            if (descDiv) {
              li.appendChild(descDiv);
            }
            ul.appendChild(a);
          }
        });
      } else {
        var li = document.createElement('li');
        li.setAttribute('class', 'dropdown-item');
        li.innerText = 'No results found';
        ul.appendChild(li);
      }

      while (searchMenu.hasChildNodes()) {
        searchMenu.removeChild(
          searchMenu.lastChild
        );
      }
      searchMenu.appendChild(ul);
    }

    function renderSearchResultsMobile(searchText, results) {
      searchResults = document.getElementById('search-mobile-results');

      var content = document.createElement('div');
      content.setAttribute('class', 'mobile-search__content');

      if (results.length > 0) {
        results.forEach(function (result) {
          var item = document.createElement('a');
          item.setAttribute('href', result.uri);
          item.innerHTML = '<div class="mobile-search__item"><div class="mobile-search__item--title">📄 ' + result.title + '</div><div class="mobile-search__item--desc">' + (result.description ? result.description : result.content) + '</div></div>';
          content.appendChild(item);
        });
      } else {
        var item = document.createElement('span');
        content.appendChild(item);
      }

      let wrap = document.getElementById('search-mobile-results');
      while (wrap.firstChild) {
        wrap.removeChild(wrap.firstChild)
      }
      searchResults.appendChild(content);      
    }

    function renderSearchHighlightResultsMobile(searchText, results) {
      searchResults = document.getElementById('search-mobile-results');

      var ul = document.createElement('div');
      ul.setAttribute('class', 'mobile-search__content');

      if (results.length) {
        results.forEach(function (result) {
          var li = document.createElement('li');
          var a = document.createElement('a');
          var descDiv = null;

          a.setAttribute('href', result.item.uri);
          a.appendChild(li);
          li.setAttribute('class', 'mobile-search__item');

          var titleDiv = document.createElement('div');
          titleDiv.innerHTML = result.item.title;
          titleDiv.setAttribute('class', 'mobile-search__item--title');
          
          if (result.matches && result.matches.length) {
            for (var i = 0; i < result.matches.length; i++) {
              if ('title' === result.matches[i].key) {
                titleDiv.innerHTML = generateHighlightedText(result.matches[i].value, result.matches[i].indices);
              }

              if ('description' === result.matches[i].key) {
                descDiv = document.createElement('div');
                descDiv.setAttribute('class', 'mobile-search__item--desc');
                descDiv.innerHTML = generateHighlightedText(result.item.description, result.matches[i].indices);
              } else if ('content' === result.matches[i].key) {
                if (!descDiv) {
                  descDiv = document.createElement('div');
                  descDiv.setAttribute('class', 'mobile-search__item--desc');
                  descDiv.innerHTML = generateHighlightedText(result.item.content.substring(0, 150), result.matches[i].indices);
                }
              } else {
                if (result.item.description) {
                  descDiv = document.createElement('div');
                  descDiv.setAttribute('class', 'mobile-search__item--desc');
                  descDiv.innerHTML = result.item.description;
                } else {
                  descDiv = document.createElement('div');
                  descDiv.setAttribute('class', 'mobile-search__item--desc');
                  descDiv.innerHTML = result.item.content.substring(0, 150);
                }
              }
            }
            
            li.appendChild(titleDiv);
            if (descDiv) {
              li.appendChild(descDiv);
            }
            ul.appendChild(a);
          }
        });
      } else {
        var item = document.createElement('span');
        ul.appendChild(item);
      }

      let wrap = document.getElementById('search-mobile-results');
      while (wrap.firstChild) {
        wrap.removeChild(wrap.firstChild)
      }
      searchResults.appendChild(ul);
    }

    function generateHighlightedText(text, regions) {
      if (!regions) {
        return text;
      }

      var content = '', nextUnhighlightedRegionStartingIndex = 0;

      regions.forEach(function(region) {
        if (region[0] === region[1]) {
          return null;
        }
        
        content += '' +
          text.substring(nextUnhighlightedRegionStartingIndex, region[0]) +
          '<span class="search__highlight">' +
            text.substring(region[0], region[1] + 1) +
          '</span>' +
        '';
        nextUnhighlightedRegionStartingIndex = region[1] + 1;
      });

      content += text.substring(nextUnhighlightedRegionStartingIndex);

      return content;
    };

    var searchElem = document.getElementById('search');
    var searchMobile = document.getElementById('search-mobile');
    var searchResultsContainer = document.getElementById('search-results');

    searchElem ?
    searchElem.addEventListener('input', function(e) {
      if (!e.target.value | window.innerWidth < 770) {
        searchResultsContainer ? searchResultsContainer.setAttribute('class', 'dropdown') : null;
        searchResult ? searchResult.setAttribute('data-display', 'none') : null;
        summaryContainer ? summaryContainer.setAttribute('data-display', 'block') : null;
        return null;
      }

      searchText = e.target.value;
      var results = fuse.search(e.target.value);
      
      if (searchResultPosition === "main") {
        if (enableSearchHighlight) {
          renderSearchHighlightResultsMain(searchText, results);
        } else {
          renderSearchResultsMain(searchText, results);
        }
      } else {
        if (enableSearchHighlight) {
          renderSearchHighlightResultsSide(searchText, results);
        } else {
          renderSearchResultsSide(searchText, results);
        }
        
        var dropdownItems = searchResultsContainer.querySelectorAll('.dropdown-item');
        dropdownItems ? dropdownItems.forEach(function(item) {
          item.addEventListener('mousedown', function(e) {
            e.target.click();
          });
        }) : null;
      }
    }) : null;

    searchElem ? 
    searchElem.addEventListener('blur', function() {
      if (window.innerWidth < 770) {
        return null;
      }
      searchResultsContainer ? searchResultsContainer.setAttribute('class', 'dropdown') : null;
    }) : null;

    searchElem ? 
    searchElem.addEventListener('click', function(e) {
      if (window.innerWidth < 770) {
        return null;
      }
      if (!e.target.value) {
        searchResultsContainer ? searchResultsContainer.setAttribute('class', 'dropdown') : null;
        return null;
      }

      searchText = e.target.value;
      var results = fuse.search(e.target.value);

      if (searchResultPosition === "main") {
        if (enableSearchHighlight) {
          renderSearchHighlightResultsMain(searchText, results);
        } else {
          renderSearchResultsMain(searchText, results);
        }
      } else{
        if (enableSearchHighlight) {
          renderSearchHighlightResultsSide(searchText, results);
        } else {
          renderSearchResultsSide(searchText, results);
        }

        var dropdownItems = searchResultsContainer.querySelectorAll('.dropdown-item');
        dropdownItems ? dropdownItems.forEach(function (item) {
          item.addEventListener('mousedown', function (e) {
            e.target.click();
          });
        }) : null;
      }
    }) : null;

    var searchMenuElem = document.getElementById("search-menu");
    var activeItem = document.querySelector('#search-menu .dropdown-item.is-active');
    var activeIndex = null;
    var items = null;
    var searchContainerMaxHeight = 350;

    searchElem ? 
    searchElem.addEventListener('keydown', function(e) {
      if (window.innerWidth < 770) {
        return null;
      }

      if (e.key === 'Escape') {
        searchResult ? searchResult.setAttribute('data-display', 'none') : null;
        summaryContainer ? summaryContainer.setAttribute('data-display', 'block') : null;
      }

      var items = document.querySelectorAll('#search-menu .dropdown-item');
      var keyCode = e.which || e.keyCode;

      if (!items || !items.length) {
        return null;
      }
      
      if (e.key === 'ArrowDown' || keyCode === 40) {
        if (activeIndex === null) {
          activeIndex = 0;
          items[activeIndex].classList.remove('is-active');
        } else {
          items[activeIndex].classList.remove('is-active');
          activeIndex = activeIndex === items.length - 1 ? 0 : activeIndex + 1;
        }
        items[activeIndex].classList.add('is-active');

        let overflowedPixel = items[activeIndex].offsetTop + items[activeIndex].clientHeight - searchContainerMaxHeight;
        if (overflowedPixel > 0) {
          document.querySelector(".search-content").scrollTop += items[activeIndex].getBoundingClientRect().height;
        } else if (activeIndex === 0) {
          document.querySelector(".search-content").scrollTop = 0;
        }
      } else if (e.key === 'ArrowUp' || keyCode === 38) {
        if (activeIndex === null) {
          activeIndex = items.length - 1;
          items[activeIndex].classList.remove('is-active');
        } else {
          items[activeIndex].classList.remove('is-active');
          activeIndex = activeIndex === 0 ? items.length - 1 : activeIndex - 1;
        }
        items[activeIndex].classList.add('is-active');
        
        let overflowedPixel = items[activeIndex].offsetTop + items[activeIndex].clientHeight - searchContainerMaxHeight;
        if (overflowedPixel < 0) {
          document.querySelector(".search-content").scrollTop -= items[activeIndex].getBoundingClientRect().height;
        } else {
          document.querySelector(".search-content").scrollTop = overflowedPixel + items[activeIndex].getBoundingClientRect().height;
        }
      } else if (e.key === 'Enter' || keyCode === 13) {
        if (items[activeIndex] && items[activeIndex].getAttribute('href')) {
          location.href = items[activeIndex].getAttribute('href');
        }
      } else if (e.key === 'Escape' || keyCode === 27) {
        e.target.value = null;
        if (searchResults) {
          searchResults.classList.remove('is-active');
        }
      }
    }) : null;

    searchMobile ? 
    searchMobile.addEventListener('input', function(e) {
      if (!e.target.value) {
        let wrap = document.getElementById('search-mobile-results');
        while (wrap.firstChild) {
          wrap.removeChild(wrap.firstChild);
        }
        return null;
      }

      searchText = e.target.value;
      var results = fuse.search(e.target.value);
      renderSearchResultsMobile(searchText, results);
      if (enableSearchHighlight) {
        renderSearchHighlightResultsMobile(searchText, results);
      } else {
        renderSearchResultsMobile(searchText, results);
      }
    }) : null;
  


  
    var mobileSearchInputElem = document.querySelector('#search-mobile');
    var mobileSearchClassElem = document.querySelector('.mobile-search');
    var mobileSearchBtnElem = document.querySelector('#mobileSearchBtn');
    var mobileSearchCloseBtnElem = document.querySelector('#search-mobile-close');
    var mobileSearchContainer = document.querySelector('#search-mobile-container');
    var mobileSearchResultsElem = document.querySelector('#search-mobile-results');
    var htmlElem = document.querySelector('html');

    if (mobileSearchClassElem) {
      mobileSearchClassElem.style.display = 'none';
    }

    mobileSearchBtnElem ? 
    mobileSearchBtnElem.addEventListener('click', function () {
      if (mobileSearchContainer) {
        mobileSearchContainer.style.display = 'block';
      }

      if (mobileSearchInputElem) {
        mobileSearchInputElem.focus();
      }

      if (htmlElem) {
        htmlElem.style.overflowY = 'hidden';
      }
    }) : null;

    mobileSearchCloseBtnElem ? 
    mobileSearchCloseBtnElem.addEventListener('click', function() {
      if (mobileSearchContainer) {
        mobileSearchContainer.style.display = 'none';
      }

      if (mobileSearchInputElem) {
        mobileSearchInputElem.value = '';
      }
      
      if (mobileSearchResultsElem) {
        while (mobileSearchResultsElem.firstChild) {
          mobileSearchResultsElem.removeChild(mobileSearchResultsElem.firstChild);
        }
      }

      if (htmlElem) {
        htmlElem.style.overflowY = 'visible';
      }
    }) : null;

    mobileSearchInputElem ?
    mobileSearchInputElem.addEventListener('keydown', function(e) {
      var keyCode = e.which || e.keyCode;
      if (e.key === 'Escape' || keyCode === 27) {
        if (mobileSearchContainer) {
          mobileSearchContainer.style.display = 'none';
        }
        
        if (mobileSearchInputElem) {
          mobileSearchInputElem.value = '';
        }

        if (mobileSearchResultsElem) {
          while (mobileSearchResultsElem.firstChild) {
            mobileSearchResultsElem.removeChild(mobileSearchResultsElem.firstChild);
          }
        }
        if (htmlElem) {
          htmlElem.style.overflowY = 'visible';
        }
      }
    }) : null;
  


  
    function renderSearchResultsMain(searchText, results) {
      var searchBody = document.querySelector('.search-result__body');
      var originUl = searchBody.querySelector('ul');
      var ul = document.createElement('ul');
      
      if (!searchText) {
        searchResult ? searchResult.setAttribute('data-display', 'none') : null;
        summaryContainer ? summaryContainer.setAttribute('data-display', 'block') : null;
      } else if (results) {
        if (results && results.length) {
          results.forEach(function (result) {
            makeLi(ul, result);
          });

          searchResult ? searchResult.setAttribute('data-display', 'block') : null;
          summaryContainer ? summaryContainer.setAttribute('data-display', 'none') : null;
        }
      }

      originUl.parentNode.replaceChild(ul, originUl);
    }

    function renderSearchHighlightResultsMain(searchText, results) {
      var searchBody = document.querySelector('.search-result__body');
      var originUl = searchBody.querySelector('ul');
      var ul = document.createElement('ul');

      if (!searchText) {
        searchResult ? searchResult.setAttribute('data-display', 'none') : null;
        summaryContainer ? summaryContainer.setAttribute('data-display', 'block') : null;
      } else if (results) {
        if (results && results.length) {
          results.forEach(function (result) {
            makeHighlightLi(ul, result);
          });

          searchResult ? searchResult.setAttribute('data-display', 'block') : null;
          summaryContainer ? summaryContainer.setAttribute('data-display', 'none') : null;
        }
      }

      originUl.parentNode.replaceChild(ul, originUl);
    }
  
  });
</script>    
    


<link rel="stylesheet" href="/css/main.min.css">


    
<meta name="description" content="" />


<meta name="keywords" content="linux">

<meta name="created" content="2020-03-09T04:59:48&#43;0000">
<meta name="modified" content="2020-03-09T04:59:48&#43;0000">
<meta property="article:published_time" content="2020-03-09T04:59:48&#43;0000">

<meta name="author" content="月冰升">


<meta property="og:site_name" content="Hugo Zzo Theme">
<meta property="og:title" content="linux内存物理内存分配伙伴系统">
<meta property="og:url" content="http://shaddy.gitee.io/zh/linux/linuxcore/notes/linux%E5%86%85%E5%AD%98%E7%89%A9%E7%90%86%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E4%BC%99%E4%BC%B4%E7%B3%BB%E7%BB%9F/">
<meta property="og:type" content="article">
<meta property="og:description" content="">

<meta name="generator" content="Hugo 0.68.3" />
<meta name="msapplication-TileColor" content="#fff">

<meta name="theme-color" content="#fff">

<meta name="msapplication-navbutton-color" content="#fff">

<meta name="apple-mobile-web-app-status-bar-style" content="#fff">

<link rel="canonical" href="http://shaddy.gitee.io/zh/linux/linuxcore/notes/linux%E5%86%85%E5%AD%98%E7%89%A9%E7%90%86%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E4%BC%99%E4%BC%B4%E7%B3%BB%E7%BB%9F/">

<link rel="manifest" href="/manifest.json">

  
  <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
  <link rel="icon" href="/favicon.png" sizes="any" type="image/png" />
  


    <script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "WebPage",
    "headline": "linux内存物理内存分配伙伴系统",
    "datePublished": "2020-03-09T04:59:48Z",
    "dateModified": "2020-03-09T04:59:48Z",
    "url" : "http://shaddy.gitee.io/zh/linux/linuxcore/notes/linux%E5%86%85%E5%AD%98%E7%89%A9%E7%90%86%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E4%BC%99%E4%BC%B4%E7%B3%BB%E7%BB%9F/",
    "description": ,
    "keywords": ["linux"],
    "mainEntityOfPage": {
      "@type": "WebPage",
      "@id": "http://shaddy.gitee.io/"
    },
    "publisher": {
      "@type": "Organization",
      "name": "Hugo Zzo Theme",
      "url": "http://shaddy.gitee.io/"
    }
  }
</script>

    
  
  







    
</head>

<body id="root" class="theme__dark">
    <script>
        var localTheme = localStorage.getItem('theme');
        if (localTheme) {
            document.getElementById('root').className = 'theme__' + localTheme;
        }
    </script>
    <div id="container">
        





        <div class="wrapper" data-type="linux" data-kind="page">
            <nav class="navbar" role="navigation" aria-label="main navigation" data-dir="ltr">
  <div class="navbar__brand">
    
    <a href="/zh/" title="主页" rel="home" class="navbar__logo-link">
      <img src="/logo.png" alt="Home" class="navbar__logo">
    </a>
    
    
      <a href="/zh/" title="主页" rel="home" class="navbar__title-link">
        <h6 class="navbar__title">HOME</h6>
      </a>
    
  </div>

  
<div class="theme theme-mobile" data-ani="true">
  <div class="dropdown">
    <button class="dropdown-trigger navbar__slide-down" aria-label="Select Theme Button" style="" data-ani="true">
      <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="none" d="M24 0H0v24h24V0z"/><path fill="currentColor" d="M6.34 7.93c-3.12 3.12-3.12 8.19 0 11.31C7.9 20.8 9.95 21.58 12 21.58s4.1-.78 5.66-2.34c3.12-3.12 3.12-8.19 0-11.31l-4.95-4.95c-.39-.39-1.02-.39-1.41 0L6.34 7.93zM12 19.59c-1.6 0-3.11-.62-4.24-1.76C6.62 16.69 6 15.19 6 13.59s.62-3.11 1.76-4.24L12 5.1v14.49z"/></svg>      
    </button>
    <div class="dropdown-content select-theme">
      
        
        <a href="#" class="dropdown-item select-theme__item is-active">
          dark
        </a>
        
        <a href="#" class="dropdown-item select-theme__item ">
          light
        </a>
        
        <a href="#" class="dropdown-item select-theme__item ">
          hacker
        </a>
        
        <a href="#" class="dropdown-item select-theme__item ">
          solarized
        </a>
        
        <a href="#" class="dropdown-item select-theme__item ">
          kimbie
        </a>
        
      
    </div>
  </div>
</div>


<div id="mobileSearchBtn" class="mobile-search__btn" data-ani="true">
  <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" fill="currentColor" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M15.5 14h-.79l-.28-.27c1.2-1.4 1.82-3.31 1.48-5.34-.47-2.78-2.79-5-5.59-5.34-4.23-.52-7.79 3.04-7.27 7.27.34 2.8 2.56 5.12 5.34 5.59 2.03.34 3.94-.28 5.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
</div>

<div id="search-mobile-container" class="mobile-search hide" data-dir="ltr">
  <div class="mobile-search__top">
    <input id="search-mobile" type="text" aria-label="Mobile Search" placeholder="搜索" class="mobile-search__top--input"/>
    <div id="search-mobile-close" class="mobile-search__top--icon">
      <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path opacity=".87" fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm3.59-13L12 10.59 8.41 7 7 8.41 10.59 12 7 15.59 8.41 17 12 13.41 15.59 17 17 15.59 13.41 12 17 8.41z"/></svg>
    </div>
  </div>
  <div id="search-mobile-results" class="mobile-search__body">
    
  </div>
</div>


<a role="button" class="navbar__burger" aria-label="menu" aria-expanded="false"
  data-ani="true">
  <span aria-hidden="true"></span>
  <span aria-hidden="true"></span>
  <span aria-hidden="true"></span>
</a>
<div class="navbarm__collapse" data-open="false">
  <ul dir="ltr">
    
    
      
      
      
      

      
        <li class="navbarm__menu--item ">
          <a href="/zh/about">about</a>
        </li>
      
      
    
      
      
      
      

      
        <li class="navbarm__menu--item ">
          <a href="/zh/archive">archive</a>
        </li>
      
      
    
      
      
      
      

      
        <li class="navbarm__menu--item ">
          <a href="/zh/gallery">
            gallery
            <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24"><path fill="currentColor" d="M8.12 9.29L12 13.17l3.88-3.88c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41l-4.59 4.59c-.39.39-1.02.39-1.41 0L6.7 10.7c-.39-.39-.39-1.02 0-1.41.39-.38 1.03-.39 1.42 0z"/></svg>
          </a>
        </li>

        
          <li class="navbarm__menu--item navbarm__menu--subitem">
            <a href="/zh/gallery/cartoon">cartoon</a>
          </li>
        
          <li class="navbarm__menu--item navbarm__menu--subitem">
            <a href="/zh/gallery/photo">photo</a>
          </li>
        

      
      
    
      
      
      
      

      
        <li class="navbarm__menu--item ">
          <a href="/zh/posts">posts</a>
        </li>
      
      
    
      
      
      
      

      
        <li class="navbarm__menu--item ">
          <a href="/zh/notes">notes</a>
        </li>
      
      
    

    
  </ul>
</div>
  <div class="navbar__menu">
  
<div class="theme" data-ani="true">
  <div class="dropdown">
    <button class="dropdown-trigger navbar__slide-down" aria-label="Select Theme Button" data-ani="true">
      <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="none" d="M24 0H0v24h24V0z"/><path fill="currentColor" d="M6.34 7.93c-3.12 3.12-3.12 8.19 0 11.31C7.9 20.8 9.95 21.58 12 21.58s4.1-.78 5.66-2.34c3.12-3.12 3.12-8.19 0-11.31l-4.95-4.95c-.39-.39-1.02-.39-1.41 0L6.34 7.93zM12 19.59c-1.6 0-3.11-.62-4.24-1.76C6.62 16.69 6 15.19 6 13.59s.62-3.11 1.76-4.24L12 5.1v14.49z"/></svg>      
    </button>
    <div class="dropdown-content select-theme">
      
        
        <a href="#" class="dropdown-item select-theme__item is-active">
          dark
        </a>
        
        <a href="#" class="dropdown-item select-theme__item ">
          light
        </a>
        
        <a href="#" class="dropdown-item select-theme__item ">
          hacker
        </a>
        
        <a href="#" class="dropdown-item select-theme__item ">
          solarized
        </a>
        
        <a href="#" class="dropdown-item select-theme__item ">
          kimbie
        </a>
        
      
    </div>
  </div>
</div>

  
  
  
  
  
  
  
  <a href="/zh/about" class="navbar__menu-item navbar__slide-down " dir="ltr" data-ani="true">about</a>
  
  
  
  
  
  
  
  <a href="/zh/archive" class="navbar__menu-item navbar__slide-down " dir="ltr" data-ani="true">archive</a>
  
  
  
  
  
  
  
  <div class="navbar__dropdown navbar__slide-down" data-ani="true">
    <a href="/zh/gallery" class="navbar__menu-item "
      dir="ltr">
      gallery
      <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24"><path fill="currentColor" d="M8.12 9.29L12 13.17l3.88-3.88c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41l-4.59 4.59c-.39.39-1.02.39-1.41 0L6.7 10.7c-.39-.39-.39-1.02 0-1.41.39-.38 1.03-.39 1.42 0z"/></svg>
    </a>
    <div class="navbar__dropdown--content">
      
      <a href="/zh/gallery/cartoon" class="navbar__dropdown--item" dir="ltr">cartoon</a>
      
      <a href="/zh/gallery/photo" class="navbar__dropdown--item" dir="ltr">photo</a>
      
    </div>
  </div>
  
  
  
  
  
  
  
  <a href="/zh/posts" class="navbar__menu-item navbar__slide-down " dir="ltr" data-ani="true">posts</a>
  
  
  
  
  
  
  
  <a href="/zh/notes" class="navbar__menu-item navbar__slide-down " dir="ltr" data-ani="true">notes</a>
  
  
</div>
</nav>
            
            

<main class="single__main main-main">
  
    <nav class="breadcrumb hide" aria-label="breadcrumbs">
  <script>document.querySelector('.breadcrumb').classList.remove('hide')</script>
  <ol>
    
  
  
  
  
  
  <li >
    
      <a href="http://shaddy.gitee.io/zh/" class="capitalize">Hugo Zzo Theme</a>
    
  </li>
  
  
  <li >
    
      <a href="http://shaddy.gitee.io/zh/linux/" class="capitalize">Linuxes</a>
    
  </li>
  
  
  <li  class="is-active" >
    
      <span>linux内存物理内存分配伙伴系统</span>
    
  </li>
  
  </ol>
  
</nav>
  
  
  <div class="single ">
    <div class="single__nojs">Please enable Javascript to view the contents</div>
    <script>document.querySelector('.single').classList.remove('hide'); document.querySelector('.single__nojs').classList.add('hide');</script>
    <h2 class="single__title" data-ani="true">linux内存物理内存分配伙伴系统</h2>
    <div class="single__meta">
      
<div class="single__infos">
  <time class="single__info" title="创建日期">📅&nbsp;2020年03月09日 </time>
  
  &nbsp;&middot;&nbsp; <span class="single__info" title="阅读时长"> ☕&nbsp;35&nbsp;分钟 </span>
  
  <span class="single__info">
    
  </span>
</div>
      
<ul class="single__tags caption">
  
  🏷️
  

  <li><a href="http://shaddy.gitee.io/zh/tags/linux/" class="single__tag" title="linux">#linux</a></li>

</ul>
    </div>
    <article class="single__contents" data-dir="ltr" data-ani="true">
      
      <blockquote>
<p>linux内存物理内存分配伙伴系统</p>
</blockquote>
<!-- more -->
<p><strong>介绍</strong><br />
在内核初始化完成之后, 内存管理的责任就由伙伴系统来承担. 伙伴系统基于一种相对简单然而令人吃惊的强大算法.<br />
Linux内核使用二进制伙伴算法来管理和分配物理内存页面, 该算法由Knowlton设计, 后来Knuth又进行了更深刻的描述.<br />
伙伴系统是一个结合了2的方幂个分配器和空闲缓冲区合并计技术的内存分配方案, 其基本思想很简单. 内存被分成含有很多页面的大块, 每一块都是2个页面大小的方幂. 如果找不到想要的块, 一个大块会被分成两部分, 这两部分彼此就成为伙伴. 其中一半被用来分配, 而另一半则空闲. 这些块在以后分配的过程中会继续被二分直至产生一个所需大小的块. 当一个块被最终释放时, 其伙伴将被检测出来, 如果伙伴也空闲则合并两者.</p>
<ul>
<li>内核如何记住哪些内存块是空闲的</li>
<li>分配空闲页面的方法</li>
<li>影响分配器行为的众多标识位</li>
<li>内存碎片的问题和分配器如何处理碎片</li>
</ul>
<h3 id="伙伴系统数据结构">伙伴系统数据结构</h3>
<h4 id="数据结构">数据结构</h4>
<p>系统内存中的每个物理内存页（页帧），都对应于一个struct page实例, 每个内存域都关联了一个struct zone的实例，其中保存了用于管理伙伴数据的主要数数组。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="k">struct</span> <span class="nc">zone</span>
<span class="p">{</span>
     <span class="cm">/* free areas of different sizes */</span>
    <span class="k">struct</span> <span class="nc">free_area_t</span>        <span class="n">free_area</span><span class="p">[</span><span class="n">MAX_ORDER</span><span class="p">];</span>
<span class="p">};</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="nc">free_area_struct</span><span class="p">{</span>
  <span class="k">struct</span> <span class="nc">list_head</span> <span class="n">free_list</span><span class="p">;</span> <span class="err">#空闲页面块链表</span>  <span class="n">page</span><span class="o">-&gt;</span><span class="n">list</span>
  <span class="kt">unsigned</span> <span class="kt">long</span> <span class="o">*</span> <span class="n">map</span><span class="p">;</span> <span class="err">#伙伴状态的位图</span>
<span class="p">}</span> <span class="n">free_area_t</span><span class="p">;</span>

<span class="err">另外一种</span>
<span class="k">struct</span> <span class="nc">free_area</span> <span class="p">{</span>
    <span class="k">struct</span> <span class="nc">list_head</span>        <span class="n">free_list</span><span class="p">[</span><span class="n">MIGRATE_TYPES</span><span class="p">];</span>
    <span class="kt">unsigned</span> <span class="kt">long</span>           <span class="n">nr_free</span><span class="p">;</span>
<span class="p">};</span>

</code></pre></td></tr></table>
</div>
</div><p>伙伴系统的分配器维护空闲页面所组成的块, 这里每一块都是2的方幂个页面, 方幂的指数称为阶.<br />
阶是伙伴系统中一个非常重要的术语. 它描述了内存分配的数量单位. 内存块的长度是2^0,order , 其中order的范围从0到MAX_ORDER<br />
zone-&gt;free_area[MAX_ORDER]数组中阶作为各个元素的索引, 用于指定对应链表中的连续内存区包含多少个页帧.</p>
<ul>
<li>数组中第0个元素的阶为0, 它的free_list链表域指向具有包含区为单页(2^0 = 1)的内存页面链表</li>
<li>数组中第1个元素的free_list域管理的内存区为两页(2^1 = 2)</li>
<li>第3个管理的内存区为4页, 依次类推.</li>
<li>直到 2^MAXORDER-1个页面大小的块<br />
<img src="" alt="" /></li>
</ul>
<p>map的位切换如下</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cp">#define MARK_USED(index,order,area)\
</span><span class="cp"></span><span class="n">__change_bit</span><span class="p">((</span><span class="n">index</span><span class="p">)</span><span class="o">&gt;&gt;</span><span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="p">(</span><span class="n">order</span><span class="p">)),(</span><span class="n">area</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">map</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><p>index 就是全局的mem_map数组中的页面下表。将它右移动1+order位就可以得到代表伙伴对的映射中的位。</p>
<h4 id="最大阶max_order与force_max_zoneorder配置选项">最大阶MAX_ORDER与FORCE_MAX_ZONEORDER配置选项</h4>
<p>一般来说MAX_ORDER默认定义为11, 这意味着一次分配可以请求的页数最大是2^11=2048, 参见include/linux/mmzone.h?v=4.7, line 22</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cm">/* Free memory management - zoned buddy allocator.  */</span>
<span class="cp">#ifndef CONFIG_FORCE_MAX_ZONEORDER
</span><span class="cp">#define MAX_ORDER 11
</span><span class="cp">#else
</span><span class="cp">#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER
</span><span class="cp">#endif
</span><span class="cp">#define MAX_ORDER_NR_PAGES (1 &lt;&lt; (MAX_ORDER - 1))
</span></code></pre></td></tr></table>
</div>
</div><p>但如果特定于体系结构的代码设置了FORCE_MAX_ZONEORDER配置选项, 该值也可以手工改变<br />
例如，IA-64系统上巨大的地址空间可以处理MAX_ORDER = 18的情形，而ARM或v850系统则使用更小的值(如8或9). 但这不一定是由计算机支持的内存数量比较小引起的，也可能是内存对齐方式的要求所导致.<br />
比如arm64体系结构的Kconfig配置文件的描述</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="n">config</span> <span class="n">FORCE_MAX_ZONEORDER</span>
<span class="kt">int</span>
<span class="k">default</span> <span class="s">&#34;14&#34;</span> <span class="k">if</span> <span class="p">(</span><span class="n">ARM64_64K_PAGES</span> <span class="o">&amp;&amp;</span> <span class="n">TRANSPARENT_HUGEPAGE</span><span class="p">)</span>
<span class="k">default</span> <span class="s">&#34;12&#34;</span> <span class="k">if</span> <span class="p">(</span><span class="n">ARM64_16K_PAGES</span> <span class="o">&amp;&amp;</span> <span class="n">TRANSPARENT_HUGEPAGE</span><span class="p">)</span>
<span class="k">default</span> <span class="s">&#34;11&#34;</span>
</code></pre></td></tr></table>
</div>
</div><h3 id="内存区是如何连接的">内存区是如何连接的</h3>
<p>内存区中第1页内的链表元素, 可用于将内存区维持在链表中。因此，也不必引入新的数据结构来管理物理上连续的页，否则这些页不可能在同一内存区中. 如下图所示<br />
<img src="" alt="" /><br />
伙伴不必是彼此连接的. 如果一个内存区在分配其间分解为两半, 内核会自动将未用的一半加入到对应的链表中.<br />
如果在未来的某个时刻, 由于内存释放的缘故, 两个内存区都处于空闲状态, 可通过其地址判断其是否为伙伴. 管理工作较少, 是伙伴系统的一个主要优点.<br />
基于伙伴系统的内存管理专注于某个结点的某个内存域, 例如, DMA或高端内存域. 但所有内存域和结点的伙伴系统都通过备用分配列表连接起来.<br />
下图说明了这种关系.<br />
<img src="" alt="" /><br />
最后要注意, 有关伙伴系统和当前状态的信息可以在/proc/buddyinfo中获取<br />
<img src="" alt="" /></p>
<p>内核中很多时候要求分配连续页. 为快速检测内存中的连续区域, 内核采用了一种古老而历经检验的技术: 伙伴系统<br />
系统中的空闲内存块总是两两分组, 每组中的两个内存块称作伙伴. 伙伴的分配可以是彼此独立的. 但如果两个伙伴都是空闲的, 内核会将其合并为一个更大的内存块, 作为下一层次上某个内存块的伙伴.<br />
下图示范了该系统, 图中给出了一对伙伴, 初始大小均为8页. 即系统中所有的页面都是8页的.<br />
<img src="" alt="" /><br />
内核对所有大小相同的伙伴（1、2、4、8、16或其他数目的页），都放置到同一个列表中管理. 各有8页的一对伙伴也在相应的列表中.<br />
如果系统现在需要8个页帧, 则将16个页帧组成的块拆分为两个伙伴. 其中一块用于满足应用程序的请求, 而剩余的8个页帧则放置到对应8页大小内存块的列表中.<br />
如果下一个请求只需要2个连续页帧, 则由8页组成的块会分裂成2个伙伴, 每个包含4个页帧. 其中一块放置回伙伴列表中，而另一个再次分裂成2个伙伴, 每个包含2页。其中一个回到伙伴系统，另一个则传递给应用程序.<br />
在应用程序释放内存时, 内核可以直接检查地址, 来判断是否能够创建一组伙伴, 并合并为一个更大的内存块放回到伙伴列表中, 这刚好是内存块分裂的逆过程。这提高了较大内存块可用的可能性.<br />
在系统长期运行时，服务器运行几个星期乃至几个月是很正常的，许多桌面系统也趋向于长期开机运行，那么会发生称为碎片的内存管理问题。频繁的分配和释放页帧可能导致一种情况：系统中有若干页帧是空闲的，但却散布在物理地址空间的各处。换句话说，系统中缺乏连续页帧组成的较大的内存块，而从性能上考虑，却又很需要使用较大的连续内存块。通过伙伴系统可以在某种程度上减少这种效应，但无法完全消除。如果在大块的连续内存中间刚好有一个页帧分配出去，很显然这两块空闲的内存是无法合并的.<br />
在内核版本2.6.24之后, 增加了一些有效措施来防止内存碎片.</p>
<h3 id="避免碎片">避免碎片</h3>
<p>在第1章给出的简化说明中, 一个双链表即可满足伙伴系统的所有需求. 在内核版本2.6.23之前, 的确是这样. 但在内核2.6.24开发期间, 内核开发者对伙伴系统的争论持续了相当长时间. 这是因为伙伴系统是内核最值得尊敬的一部分，对它的改动不会被大家轻易接受</p>
<h4 id="内存碎片">内存碎片</h4>
<p>伙伴系统的基本原理已经在第1章中讨论过，其方案在最近几年间确实工作得非常好。但在Linux内存管理方面，有一个长期存在的问题：在系统启动并长期运行后，物理内存会产生很多碎片。该情形如下图所示<br />
<img src="" alt="" /><br />
假定内存由60页组成，这显然不是超级计算机，但用于示例却足够了。左侧的地址空间中散布着空闲页。尽管大约25%的物理内存仍然未分配，但最大的连续空闲区只有一页. 这对用户空间应用程序没有问题：其内存是通过页表映射的，无论空闲页在物理内存中的分布如何，应用程序看到的内存 似乎总是连续的。右图给出的情形中，空闲页和使用页的数目与左图相同，但所有空闲页都位于一个连续区中。<br />
但对内核来说，碎片是一个问题. 由于(大多数)物理内存一致映射到地址空间的内核部分, 那么在左图的场景中, 无法映射比一页更大的内存区. 尽管许多时候内核都分配的是比较小的内存, 但也有时候需要分配多于一页的内存. 显而易见, 在分配较大内存的情况下, 右图中所有已分配页和空闲页都处于连续内存区的情形，是更为可取的.<br />
很有趣的一点是, 在大部分内存仍然未分配时, 就也可能发生碎片问题. 考虑图3-25的情形.<br />
只分配了4页，但可分配的最大连续区只有8页，因为伙伴系统所能工作的分配范围只能是2的幂次.<br />
<img src="" alt="" /></p>
<p>我提到内存碎片只涉及内核，这只是部分正确的。大多数现代CPU都提供了使用巨型页的可能性，比普通页大得多。这对内存使用密集的应用程序有好处。在使用更大的页时，地址转换后备缓冲器只需处理较少的项，降低了TLB缓存失效的可能性。但分配巨型页需要连续的空闲物理内存！<br />
很长时间以来，物理内存的碎片确实是Linux的弱点之一。尽管已经提出了许多方法，但没有哪个方法能够既满足Linux需要处理的各种类型工作负荷提出的苛刻需求，同时又对其他事务影响不大。</p>
<h4 id="依据可移动性组织页">依据可移动性组织页</h4>
<p>在内核2.6.24开发期间，防止碎片的方法最终加入内核。在我讨论具体策略之前，有一点需要澄清。<br />
文件系统也有碎片，该领域的碎片问题主要通过碎片合并工具解决。它们分析文件系统，重新排序已分配存储块，从而建立较大的连续存储区. 理论上，该方法对物理内存也是可能的，但由于许多物理内存页不能移动到任意位置，阻碍了该方法的实施。因此，内核的方法是反碎片(anti-fragmentation), 即试图从最初开始尽可能防止碎片.<br />
<strong>反碎片的工作原理如何?</strong><br />
为理解该方法，我们必须知道内核将已分配页划分为下面3种不同类型。</p>
<table>
<thead>
<tr>
<th>页面类型</th>
<th>描述</th>
<th>举例</th>
</tr>
</thead>
<tbody>
<tr>
<td>不可移动页</td>
<td>在内存中有固定位置, 不能移动到其他地方.</td>
<td>核心内核分配的大多数内存属于该类别</td>
</tr>
<tr>
<td>可移动页</td>
<td>可以随意地移动. 属于用户空间应用程序的页属于该类别.</td>
<td>它们是通过页表映射的如果它们复制到新位置，页表项可以相应地更新，应用程序不会注意到任何事</td>
</tr>
<tr>
<td>可回收页</td>
<td>不能直接移动, 但可以删除, 其内容可以从某些源重新生成.</td>
<td>例如，映射自文件的数据属于该类别。kswapd守护进程会根据可回收页访问的频繁程度，周期性释放此类内存, 页面回收本身就是一个复杂的过程. 内核会在可回收页占据了太多内存时进行回收, 在内存短缺(即分配失败)时也可以发起页面回收.</td>
</tr>
<tr>
<td>页的可移动性，依赖该页属于3种类别的哪一种. 内核使用的反碎片技术, 即基于将具有相同可移动性的页分组的思想.</td>
<td></td>
<td></td>
</tr>
<tr>
<td><strong>为什么这种方法有助于减少碎片?</strong></td>
<td></td>
<td></td>
</tr>
<tr>
<td>由于页无法移动, 导致在原本几乎全空的内存区中无法进行连续分配. 根据页的可移动性, 将其分配到不同的列表中, 即可防止这种情形. 例如, 不可移动的页不能位于可移动内存区的中间, 否则就无法从该内存区分配较大的连续内存块.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>想一下, 上图中大多数空闲页都属于可回收的类别, 而分配的页则是不可移动的. 如果这些页聚集到两个不同的列表中, 如下图所示. 在不可移动页中仍然难以找到较大的连续空闲空间, 但对可回收的页, 就容易多了.</td>
<td></td>
<td></td>
</tr>
<tr>
<td><img src="" alt="" /></td>
<td></td>
<td></td>
</tr>
<tr>
<td>但要注意, 从最初开始, 内存并未划分为可移动性不同的区. 这些是在运行时形成的. 内核的另一种方法确实将内存分区, 分别用于可移动页和不可移动页的分配, 我会下文讨论其工作原理. 但这种划分对这里描述的方法是不必要的</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<h4 id="避免碎片数据结构">避免碎片数据结构</h4>
<h5 id="迁移类型">迁移类型</h5>
<p>尽管内核使用的反碎片技术卓有成效，它对伙伴分配器的代码和数据结构几乎没有影响。内核定义了一些枚举常量(早期用宏来实现)来表示不同的迁移类型, 参见include/linux/mmzone.h?v=4.7, line 38</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="k">enum</span> <span class="p">{</span>
        <span class="n">MIGRATE_UNMOVABLE</span><span class="p">,</span>
        <span class="n">MIGRATE_MOVABLE</span><span class="p">,</span>
        <span class="n">MIGRATE_RECLAIMABLE</span><span class="p">,</span>
        <span class="n">MIGRATE_PCPTYPES</span><span class="p">,</span>       <span class="cm">/* the number of types on the pcp lists */</span>
        <span class="n">MIGRATE_HIGHATOMIC</span> <span class="o">=</span> <span class="n">MIGRATE_PCPTYPES</span><span class="p">,</span>
<span class="cp">#ifdef CONFIG_CMA
</span><span class="cp"></span>        <span class="cm">/*
</span><span class="cm">         * MIGRATE_CMA migration type is designed to mimic the way
</span><span class="cm">         * ZONE_MOVABLE works.  Only movable pages can be allocated
</span><span class="cm">         * from MIGRATE_CMA pageblocks and page allocator never
</span><span class="cm">         * implicitly change migration type of MIGRATE_CMA pageblock.
</span><span class="cm">         *
</span><span class="cm">         * The way to use it is to change migratetype of a range of
</span><span class="cm">         * pageblocks to MIGRATE_CMA which can be done by
</span><span class="cm">         * __free_pageblock_cma() function.  What is important though
</span><span class="cm">         * is that a range of pageblocks must be aligned to
</span><span class="cm">         * MAX_ORDER_NR_PAGES should biggest page be bigger then
</span><span class="cm">         * a single pageblock.
</span><span class="cm">         */</span>
        <span class="n">MIGRATE_CMA</span><span class="p">,</span>
<span class="cp">#endif
</span><span class="cp">#ifdef CONFIG_MEMORY_ISOLATION
</span><span class="cp"></span>        <span class="n">MIGRATE_ISOLATE</span><span class="p">,</span>        <span class="cm">/* can&#39;t allocate from here */</span>
<span class="cp">#endif
</span><span class="cp"></span>        <span class="n">MIGRATE_TYPES</span>
<span class="p">};</span>
</code></pre></td></tr></table>
</div>
</div><table>
<thead>
<tr>
<th>宏</th>
<th>类型</th>
</tr>
</thead>
<tbody>
<tr>
<td>MIGRATE_UNMOVABLE</td>
<td>不可移动页</td>
</tr>
<tr>
<td>MIGRATE_MOVABLE</td>
<td>可移动页</td>
</tr>
<tr>
<td>MIGRATE_RECLAIMABLE</td>
<td>可回收页</td>
</tr>
<tr>
<td>MIGRATE_PCPTYPES</td>
<td>是per_cpu_pageset, 即用来表示每CPU页框高速缓存的数据结构中的链表的迁移类型数目</td>
</tr>
<tr>
<td>MIGRATE_HIGHATOMIC</td>
<td>在罕见的情况下，内核需要分配一个高阶的页面块而不能休眠.如果向具有特定可移动性的列表请求分配内存失败，这种紧急情况下可从MIGRATE_HIGHATOMIC中分配内存</td>
</tr>
<tr>
<td>MIGRATE_CMA</td>
<td>Linux内核最新的连续内存分配器(CMA), 用于避免预留大块内存</td>
</tr>
<tr>
<td>MIGRATE_ISOLATE</td>
<td>是一个特殊的虚拟区域, 用于跨越NUMA结点移动物理内存页. 在大型系统上, 它有益于将物理内存页移动到接近于使用该页最频繁的CPU.</td>
</tr>
<tr>
<td>MIGRATE_TYPES</td>
<td>只是表示迁移类型的数目, 也不代表具体的区域</td>
</tr>
</tbody>
</table>
<p>对于MIGRATE_CMA类型, 其中在我们使用ARM等嵌入式Linux系统的时候, 一个头疼的问题是GPU, Camera, HDMI等都需要预留大量连续内存，这部分内存平时不用，但是一般的做法又必须先预留着. 目前, Marek Szyprowski和Michal Nazarewicz实现了一套全新的Contiguous Memory Allocator. 通过这套机制, 我们可以做到不预留内存，这些内存平时是可用的，只有当需要的时候才被分配给Camera，HDMI等设备. 参照宋宝华–Linux内核最新的连续内存分配器(CMA)——避免预留大块内存, 内核为此提供了函数is_migrate_cma来检测当前类型是否为MIGRATE_CMA</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cm">/* In mm/page_alloc.c; keep in sync also with show_migration_types() there */</span>
<span class="k">extern</span> <span class="kt">char</span> <span class="o">*</span> <span class="k">const</span> <span class="n">migratetype_names</span><span class="p">[</span><span class="n">MIGRATE_TYPES</span><span class="p">];</span>

<span class="cp">#ifdef CONFIG_CMA
</span><span class="cp">#  define is_migrate_cma(migratetype) unlikely((migratetype) == MIGRATE_CMA)
</span><span class="cp">#else
</span><span class="cp">#  define is_migrate_cma(migratetype) false
</span><span class="cp">#endif
</span></code></pre></td></tr></table>
</div>
</div><p>对伙伴系统数据结构的主要调整, 是将空闲列表分解为MIGRATE_TYPE个列表, 可以参见free_area的定义include/linux/mmzone.h?v=4.7, line 88</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="k">struct</span> <span class="nc">free_area</span>
<span class="p">{</span>
    <span class="k">struct</span> <span class="nc">list_head</span>        <span class="n">free_list</span><span class="p">[</span><span class="n">MIGRATE_TYPES</span><span class="p">];</span>
    <span class="kt">unsigned</span> <span class="kt">long</span>           <span class="n">nr_free</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></td></tr></table>
</div>
</div><p>nr_free统计了所有列表上空闲页的数目，而每种迁移类型都对应于一个空闲列表<br />
宏for_each_migratetype_order(order, type)可用于迭代指定迁移类型的所有分配阶</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++">
<span class="cp">#define for_each_migratetype_order(order, type) \
</span><span class="cp"></span>        <span class="k">for</span> <span class="p">(</span><span class="n">order</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">order</span> <span class="o">&lt;</span> <span class="n">MAX_ORDER</span><span class="p">;</span> <span class="n">order</span><span class="o">++</span><span class="p">)</span> <span class="err">\</span>
                <span class="k">for</span> <span class="p">(</span><span class="n">type</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">type</span> <span class="o">&lt;</span> <span class="n">MIGRATE_TYPES</span><span class="p">;</span> <span class="n">type</span><span class="o">++</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><h5 id="迁移备用列表fallbacks">迁移备用列表fallbacks</h5>
<p><strong>如果内核无法满足针对某一给定迁移类型的分配请求, 会怎么样?</strong><br />
此前已经出现过一个类似的问题, 即特定的NUMA内存域无法满足分配请求时. 我们需要从其他内存域中选择一个代价最低的内存域完成内存的分配, 因此内核在内存的结点pg_data_t中提供了一个备用内存域列表zonelists.<br />
内核在内存迁移的过程中处理这种情况下的做法是类似的. 提供了一个备用列表fallbacks, 规定了在指定列表中无法满足分配请求时. 接下来应使用哪一种迁移类型, 定义在mm/page_alloc.c?v=4.7, line 1799</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++">
<span class="cm">/*
</span><span class="cm"> * This array describes the order lists are fallen back to when
</span><span class="cm"> * the free lists for the desirable migrate type are depleted
</span><span class="cm"> * 该数组描述了指定迁移类型的空闲列表耗尽时
</span><span class="cm"> * 其他空闲列表在备用列表中的次序
</span><span class="cm"> */</span>
<span class="k">static</span> <span class="kt">int</span> <span class="n">fallbacks</span><span class="p">[</span><span class="n">MIGRATE_TYPES</span><span class="p">][</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
    <span class="c1">//  分配不可移动页失败的备用列表
</span><span class="c1"></span>    <span class="p">[</span><span class="n">MIGRATE_UNMOVABLE</span><span class="p">]</span>   <span class="o">=</span> <span class="p">{</span> <span class="n">MIGRATE_RECLAIMABLE</span><span class="p">,</span> <span class="n">MIGRATE_MOVABLE</span><span class="p">,</span>   <span class="n">MIGRATE_TYPES</span> <span class="p">},</span>
    <span class="c1">//  分配可回收页失败时的备用列表
</span><span class="c1"></span>    <span class="p">[</span><span class="n">MIGRATE_RECLAIMABLE</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="n">MIGRATE_UNMOVABLE</span><span class="p">,</span>   <span class="n">MIGRATE_MOVABLE</span><span class="p">,</span>   <span class="n">MIGRATE_TYPES</span> <span class="p">},</span>
    <span class="c1">//  分配可移动页失败时的备用列表
</span><span class="c1"></span>    <span class="p">[</span><span class="n">MIGRATE_MOVABLE</span><span class="p">]</span>     <span class="o">=</span> <span class="p">{</span> <span class="n">MIGRATE_RECLAIMABLE</span><span class="p">,</span> <span class="n">MIGRATE_UNMOVABLE</span><span class="p">,</span> <span class="n">MIGRATE_TYPES</span> <span class="p">},</span>
<span class="cp">#ifdef CONFIG_CMA
</span><span class="cp"></span>    <span class="p">[</span><span class="n">MIGRATE_CMA</span><span class="p">]</span>     <span class="o">=</span> <span class="p">{</span> <span class="n">MIGRATE_TYPES</span> <span class="p">},</span> <span class="cm">/* Never used */</span>
<span class="cp">#endif
</span><span class="cp">#ifdef CONFIG_MEMORY_ISOLATION
</span><span class="cp"></span>    <span class="p">[</span><span class="n">MIGRATE_ISOLATE</span><span class="p">]</span>     <span class="o">=</span> <span class="p">{</span> <span class="n">MIGRATE_TYPES</span> <span class="p">},</span> <span class="cm">/* Never used */</span>
<span class="cp">#endif
</span><span class="cp"></span><span class="p">};</span>
</code></pre></td></tr></table>
</div>
</div><p>该数据结构大体上是自明的 :<br />
每一行对应一个类型的备用搜索域的顺序, 在内核想要分配不可移动页MIGRATE_UNMOVABLE时, 如果对应链表为空, 则遍历fallbacks[MIGRATE_UNMOVABLE], 首先后退到可回收页链表MIGRATE_RECLAIMABLE, 接下来到可移动页链表MIGRATE_MOVABLE, 最后到紧急分配链表MIGRATE_TYPES.</p>
<h5 id="pageblock_order变量">pageblock_order变量</h5>
<p>全局变量和辅助函数尽管页可移动性分组特性总是编译到内核中，但只有在系统中有足够内存可以分配到多个迁移类型对应的链表时，才是有意义的。由于每个迁移链表都应该有适当数量的内存，内核需要定义”适当”的概念. 这是通过两个全局变量pageblock_order和pageblock_nr_pages提供的. 第一个表示内核认为是”大”的一个分配阶, pageblock_nr_pages则表示该分配阶对应的页数。如果体系结构提供了巨型页机制, 则pageblock_order通常定义为巨型页对应的分配阶. 定义在include/linux/pageblock-flags.h?v=4.7, line 44</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cp">#ifdef CONFIG_HUGETLB_PAGE
</span><span class="cp"></span>
    <span class="cp">#ifdef CONFIG_HUGETLB_PAGE_SIZE_VARIABLE
</span><span class="cp"></span>
        <span class="cm">/* Huge page sizes are variable */</span>
        <span class="k">extern</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">pageblock_order</span><span class="p">;</span>

    <span class="err">#</span><span class="k">else</span> <span class="cm">/* CONFIG_HUGETLB_PAGE_SIZE_VARIABLE */</span>

    <span class="cm">/* Huge pages are a constant size */</span>
        <span class="err">#</span><span class="n">define</span> <span class="n">pageblock_order</span>         <span class="n">HUGETLB_PAGE_ORDER</span>

    <span class="err">#</span><span class="n">endif</span> <span class="cm">/* CONFIG_HUGETLB_PAGE_SIZE_VARIABLE */</span>

<span class="cp">#else </span><span class="cm">/* CONFIG_HUGETLB_PAGE */</span><span class="cp">
</span><span class="cp"></span>
    <span class="cm">/* If huge pages are not used, group by MAX_ORDER_NR_PAGES */</span>
    <span class="cp">#define pageblock_order         (MAX_ORDER-1)
</span><span class="cp"></span>
<span class="cp">#endif </span><span class="cm">/* CONFIG_HUGETLB_PAGE */</span><span class="cp">
</span><span class="cp"></span>
<span class="cp">#define pageblock_nr_pages      (1UL &lt;&lt; pageblock_order)
</span></code></pre></td></tr></table>
</div>
</div><p>在IA-32体系结构上, 巨型页长度是4MB, 因此每个巨型页由1024个普通页组成, 而HUGETLB_PAGE_ORDER则定义为10. 相比之下, IA-64体系结构允许设置可变的普通和巨型页长度, 因此HUGETLB_PAGE_ORDER的值取决于内核配置.<br />
如果体系结构不支持巨型页, 则将其定义为第二高的分配阶, 即MAX_ORDER - 1</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cm">/* If huge pages are not used, group by MAX_ORDER_NR_PAGES */</span>
<span class="cp">#define pageblock_order         (MAX_ORDER-1)
</span></code></pre></td></tr></table>
</div>
</div><p>如果各迁移类型的链表中没有一块较大的连续内存, 那么页面迁移不会提供任何好处, 因此在可用内存太少时内核会关闭该特性. 这是在build_all_zonelists函数中检查的, 该函数用于初始化内存域列表. 如果没有足够的内存可用, 则全局变量page_group_by_mobility_disabled设置为0, 否则设置为1.<br />
内核如何知道给定的分配内存属于何种迁移类型?<br />
我们将在以后讲解, 有关各个内存分配的细节都通过分配掩码指定.<br />
内核提供了两个标志，分别用于表示分配的内存是可移动的(__GFP_MOVABLE)或可回收的(__GFP_RECLAIMABLE).</p>
<h5 id="gfpflags_to_migratetype函数">gfpflags_to_migratetype函数</h5>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cm">/* Convert GFP flags to their corresponding migrate type */</span>
<span class="k">static</span> <span class="kr">inline</span> <span class="kt">int</span> <span class="nf">allocflags_to_migratetype</span><span class="p">(</span><span class="n">gfp_t</span> <span class="n">gfp_flags</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">WARN_ON</span><span class="p">((</span><span class="n">gfp_flags</span> <span class="o">&amp;</span> <span class="n">GFP_MOVABLE_MASK</span><span class="p">)</span> <span class="o">==</span> <span class="n">GFP_MOVABLE_MASK</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">unlikely</span><span class="p">(</span><span class="n">page_group_by_mobility_disabled</span><span class="p">))</span>
        <span class="k">return</span> <span class="n">MIGRATE_UNMOVABLE</span><span class="p">;</span>

    <span class="cm">/* Group based on mobility */</span>
    <span class="k">return</span> <span class="p">(((</span><span class="n">gfp_flags</span> <span class="o">&amp;</span> <span class="n">__GFP_MOVABLE</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">|</span>
        <span class="p">((</span><span class="n">gfp_flags</span> <span class="o">&amp;</span> <span class="n">__GFP_RECLAIMABLE</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>如果停用了页面迁移特性, 则所有的页都是不可移动的. 否则. 该函数的返回值可以直接用作free_area.free_list的数组索引.</p>
<h5 id="pageblock_flags变量与其函数接口">pageblock_flags变量与其函数接口</h5>
<p>最后要注意, 每个内存域都提供了一个特殊的字段, 可以跟踪包含pageblock_nr_pages个页的内存区的属性. 即zone-&gt;pageblock_flags字段, 当前只有与页可移动性相关的代码使用, 参见include/linux/mmzone.h?v=4.7, line 367</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="k">struct</span> <span class="nc">zone</span>
<span class="p">{</span>
<span class="cp">#ifndef CONFIG_SPARSEMEM
</span><span class="cp"></span>    <span class="cm">/*
</span><span class="cm">     * Flags for a pageblock_nr_pages block. See pageblock-flags.h.
</span><span class="cm">     * In SPARSEMEM, this map is stored in struct mem_section
</span><span class="cm">     */</span>
    <span class="kt">unsigned</span> <span class="kt">long</span>       <span class="o">*</span><span class="n">pageblock_flags</span><span class="p">;</span>
<span class="cp">#endif </span><span class="cm">/* CONFIG_SPARSEMEM */</span><span class="cp">
</span><span class="cp"></span><span class="p">};</span>
</code></pre></td></tr></table>
</div>
</div><p>在初始化期间, 内核自动确保对内存域中的每个不同的迁移类型分组, 在pageblock_flags中都分配了足够存储NR_PAGEBLOCK_BITS个比特位的空间。当前，表示一个连续内存区的迁移类型需要3个比特位, 参见include/linux/pageblock-flags.h?v=4.7, line 28</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cm">/* Bit indices that affect a whole block of pages */</span>
<span class="k">enum</span> <span class="nc">pageblock_bits</span> <span class="p">{</span>
    <span class="n">PB_migrate</span><span class="p">,</span>
    <span class="n">PB_migrate_end</span> <span class="o">=</span> <span class="n">PB_migrate</span> <span class="o">+</span> <span class="mi">3</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span>
            <span class="cm">/* 3 bits required for migrate types */</span>
    <span class="n">PB_migrate_skip</span><span class="p">,</span><span class="cm">/* If set the block is skipped by compaction */</span>

    <span class="cm">/*
</span><span class="cm">     * Assume the bits will always align on a word. If this assumption
</span><span class="cm">     * changes then get/set pageblock needs updating.
</span><span class="cm">     */</span>
    <span class="n">NR_PAGEBLOCK_BITS</span>
<span class="p">};</span>
</code></pre></td></tr></table>
</div>
</div><p>内核提供set_pageblock_migratetype负责设置以page为首的一个内存区的迁移类型, 该函数定义在mm/page_alloc.c?v=4.7, line 458, 如下所示</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="kt">void</span> <span class="nf">set_pageblock_migratetype</span><span class="p">(</span><span class="k">struct</span> <span class="nc">page</span> <span class="o">*</span><span class="n">page</span><span class="p">,</span> <span class="kt">int</span> <span class="n">migratetype</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">unlikely</span><span class="p">(</span><span class="n">page_group_by_mobility_disabled</span> <span class="o">&amp;&amp;</span>
             <span class="n">migratetype</span> <span class="o">&lt;</span> <span class="n">MIGRATE_PCPTYPES</span><span class="p">))</span>
        <span class="n">migratetype</span> <span class="o">=</span> <span class="n">MIGRATE_UNMOVABLE</span><span class="p">;</span>

    <span class="n">set_pageblock_flags_group</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span><span class="n">migratetype</span><span class="p">,</span>
                    <span class="n">PB_migrate</span><span class="p">,</span> <span class="n">PB_migrate_end</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>migratetype参数可以通过上文介绍的gfpflags_to_migratetype辅助函数构建. 请注意很重要的一点, 页的迁移类型是预先分配好的, 对应的比特位总是可用, 与页是否由伙伴系统管理无关. 在释放内存时，页必须返回到正确的迁移链表。这之所以可行，是因为能够从get_pageblock_migratetype获得所需的信息. 参见include/linux/mmzone.h?v=4.7, line 84</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cp">#define get_pageblock_migratetype(page)                                 \
</span><span class="cp"></span>        <span class="n">get_pfnblock_flags_mask</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">page_to_pfn</span><span class="p">(</span><span class="n">page</span><span class="p">),</span>                <span class="err">\</span>
                        <span class="n">PB_migrate_end</span><span class="p">,</span> <span class="n">MIGRATETYPE_MASK</span><span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><p>最后请注意, 在各个迁移链表之间, 当前的页面分配状态可以从/proc/pagetypeinfo获得.</p>
<h5 id="初始化基于可移动性的分组">初始化基于可移动性的分组</h5>
<p>在内存子系统初始化期间, memmap_init_zone负责处理内存域的page实例. 该函数定义在mm/page_alloc.c?v=4.7, line 5139, 该函数完成了一些不怎么有趣的标准初始化工作，但其中有一件是实质性的，即所有的页最初都标记为可移动的. 参见mm/page_alloc.c?v=4.7, line 5224</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cm">/*
</span><span class="cm"> * Initially all pages are reserved - free ones are freed
</span><span class="cm"> * up by free_all_bootmem() once the early boot process is
</span><span class="cm"> * done. Non-atomic initialization, single-pass.
</span><span class="cm"> */</span>
<span class="kt">void</span> <span class="n">__meminit</span> <span class="nf">memmap_init_zone</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">size</span><span class="p">,</span> <span class="kt">int</span> <span class="n">nid</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">zone</span><span class="p">,</span>
        <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">start_pfn</span><span class="p">,</span> <span class="k">enum</span> <span class="nc">memmap_context</span> <span class="n">context</span><span class="p">)</span>
<span class="p">{</span>
    <span class="cm">/*  ......  */</span>

    <span class="k">for</span> <span class="p">(</span><span class="n">pfn</span> <span class="o">=</span> <span class="n">start_pfn</span><span class="p">;</span> <span class="n">pfn</span> <span class="o">&lt;</span> <span class="n">end_pfn</span><span class="p">;</span> <span class="n">pfn</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="cm">/*  ......  */</span>
<span class="nl">not_early</span><span class="p">:</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">pfn</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">pageblock_nr_pages</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)))</span> <span class="p">{</span>
            <span class="k">struct</span> <span class="nc">page</span> <span class="o">*</span><span class="n">page</span> <span class="o">=</span> <span class="n">pfn_to_page</span><span class="p">(</span><span class="n">pfn</span><span class="p">);</span>

            <span class="n">__init_single_page</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">pfn</span><span class="p">,</span> <span class="n">zone</span><span class="p">,</span> <span class="n">nid</span><span class="p">);</span>
            <span class="n">set_pageblock_migratetype</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">MIGRATE_MOVABLE</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">__init_single_pfn</span><span class="p">(</span><span class="n">pfn</span><span class="p">,</span> <span class="n">zone</span><span class="p">,</span> <span class="n">nid</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>在分配内存时, 如果必须”盗取”不同于预定迁移类型的内存区, 内核在策略上倾向于”盗取”更大的内存区. 由于所有页最初都是可移动的, 那么在内核分配不可移动的内存区时, 则必须”盗取”.<br />
实际上, 在启动期间分配可移动内存区的情况较少, 那么分配器有很高的几率分配长度最大的内存区, 并将其从可移动列表转换到不可移动列表. 由于分配的内存区长度是最大的, 因此不会向可移动内存中引入碎片.<br />
总而言之, 这种做法避免了启动期间内核分配的内存(经常在系统的整个运行时间都不释放)散布到物理内存各处, 从而使其他类型的内存分配免受碎片的干扰，这也是页可移动性分组框架的最重要的目标之一.</p>
<h3 id="分配器api">分配器API</h3>
<h4 id="分配内存的接口">分配内存的接口</h4>
<p>就伙伴系统的接口而言, NUMA或UMA体系结构是没有差别的, 二者的调用语法都是相同的.<br />
所有函数的一个共同点是 : 只能分配2的整数幂个页.<br />
因此，接口中不像C标准库的malloc函数或bootmem和memblock分配器那样指定了所需内存大小作为参数. 相反, 必须指定的是分配阶, 伙伴系统将在内存中分配2^0 rder页. 内核中细粒度的分配只能借助于slab分配器(或者slub、slob分配器), 后者基于伙伴系统</p>
<table>
<thead>
<tr>
<th>内存分配函数</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td>alloc_pages(mask, order)</td>
<td>分配2^0页并返回一个struct page的实例，表示分配的内存块的起始页</td>
</tr>
<tr>
<td>alloc_page(mask)</td>
<td>是前者在order = 0情况下的简化形式，只分配一页</td>
</tr>
<tr>
<td>get_zeroed_page(mask)</td>
<td>分配一页并返回一个page实例，页对应的内存填充0（所有其他函数，分配之后页的内容是未定义的）</td>
</tr>
<tr>
<td>__get_free_page(mask)</td>
<td>工作方式与上述函数相同，但返回分配内存块的虚拟地址，而不是page实例</td>
</tr>
<tr>
<td>get_dma_pages(gfp_mask, order)</td>
<td>用来获得适用于DMA的页.</td>
</tr>
<tr>
<td>在空闲内存无法满足请求以至于分配失败的情况下，所有上述函数都返回空指针(比如alloc_pages和alloc_page)或者0(比如get_zeroed_page、__get_free_pages和__get_free_page).</td>
<td></td>
</tr>
<tr>
<td>因此内核在各次分配之后都必须检查返回的结果. 这种惯例与设计得很好的用户层应用程序没什么不同, 但在内核中忽略检查会导致严重得多的故障</td>
<td></td>
</tr>
<tr>
<td>内核除了伙伴系统函数之外, 还提供了其他内存管理函数. 它们以伙伴系统为基础, 但并不属于伙伴分配器自身. 这些函数包括vmalloc和vmalloc_32, 使用页表将不连续的内存映射到内核地址空间中, 使之看上去是连续的.</td>
<td></td>
</tr>
<tr>
<td>还有一组kmalloc类型的函数, 用于分配小于一整页的内存区. 其实现.</td>
<td></td>
</tr>
</tbody>
</table>
<h4 id="释放函数">释放函数</h4>
<p>有4个函数用于释放不再使用的页，与所述函数稍有不同<br />
内存释放函数|	描述<br />
-|-<br />
free_page(struct page )|<br />
free_pages(struct page , order)|	用于将一个或2order页返回给内存管理子系统。内存区的起始地址由指向该内存区的第一个page实例的指针表示<br />
__free_page(addr)|<br />
__free_pages(addr, order)|	类似于前两个函数，但在表示需要释放的内存区时，使用了虚拟内存地址而不是page实例</p>
<h4 id="分配掩码gfp_mask标志">分配掩码(gfp_mask标志)</h4>
<h5 id="分配掩码">分配掩码</h5>
<p>前述所有函数中强制使用的mask参数，到底是什么语义?<br />
我们知道Linux将内存划分为内存域. 内核提供了所谓的内存域修饰符(zone modifier)(在掩码的最低4个比特位定义), 来指定从哪个内存域分配所需的页.<br />
内核使用宏的方式定义了这些掩码, 一个掩码的定义被划分为3个部分进行定义, 我们会逐步展开来讲解, 参见include/linux/gfp.h?v=4.7, line 12~374, 共计26个掩码信息, 因此后面__GFP_BITS_SHIFT = 26.</p>
<h5 id="掩码分类">掩码分类</h5>
<p>Linux中这些掩码标志gfp_mask分为3种类型 :</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>区描述都符</td>
<td>内核把物理内存分为多个区, 每个区用于不同的目的, 区描述符指明到底从这些区中的哪一区进行分配</td>
</tr>
<tr>
<td>行为修饰符</td>
<td>表示内核应该如何分配所需的内存. 在某些特定情况下, 只能使用某些特定的方法分配内存</td>
</tr>
<tr>
<td>类型标志</td>
<td>组合了行为修饰符和区描述符, 将这些可能用到的组合归纳为不同类型</td>
</tr>
</tbody>
</table>
<p>#####内核中掩码的定义<br />
内核中的定义方式</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="c1">//  http://lxr.free-electrons.com/source/include/linux/gfp.h?v=4.7
</span><span class="c1"></span>
<span class="cm">/*  line 12 ~ line 44  第一部分
</span><span class="cm"> *  定义可掩码所在位的信息, 每个掩码对应一位为1
</span><span class="cm"> *  定义形式为  #define  ___GFP_XXX      0x01u
</span><span class="cm"> */</span>
<span class="cm">/* Plain integer GFP bitmasks. Do not use this directly. */</span>
<span class="cp">#define ___GFP_DMA              0x01u
</span><span class="cp">#define ___GFP_HIGHMEM          0x02u
</span><span class="cp">#define ___GFP_DMA32            0x04u
</span><span class="cp">#define ___GFP_MOVABLE          0x08u
</span><span class="cp"></span><span class="cm">/*  ......  */</span>

<span class="cm">/*  line 46 ~ line 192  第二部分
</span><span class="cm"> *  定义掩码和MASK信息, 第二部分的某些宏可能是第一部分一个或者几个的组合
</span><span class="cm"> *  定义形式为  #define  __GFP_XXX        ((__force gfp_t)___GFP_XXX)
</span><span class="cm"> */</span>
<span class="cp">#define __GFP_DMA       ((__force gfp_t)___GFP_DMA)
</span><span class="cp">#define __GFP_HIGHMEM   ((__force gfp_t)___GFP_HIGHMEM)
</span><span class="cp">#define __GFP_DMA32     ((__force gfp_t)___GFP_DMA32)
</span><span class="cp">#define __GFP_MOVABLE   ((__force gfp_t)___GFP_MOVABLE)  </span><span class="cm">/* ZONE_MOVABLE allowed */</span><span class="cp">
</span><span class="cp">#define GFP_ZONEMASK    (__GFP_DMA|__GFP_HIGHMEM|__GFP_DMA32|__GFP_MOVABLE)
</span><span class="cp"></span>
<span class="cm">/*  line 194 ~ line 260  第三部分
</span><span class="cm"> *  定义掩码
</span><span class="cm"> *  定义形式为  #define  GFP_XXX      __GFP_XXX
</span><span class="cm"> */</span>
<span class="cp">#define GFP_DMA         __GFP_DMA
</span><span class="cp">#define GFP_DMA32       __GFP_DMA32
</span></code></pre></td></tr></table>
</div>
</div><p>其中GFP缩写的意思为获取空闲页(get free page), __GFP_MOVABLE不表示物理内存域, 但通知内核应在特殊的虚拟内存域ZONE_MOVABLE进行相应的分配.<br />
<strong>定义掩码位</strong></p>
<p>我们首先来看第一部分, 内核源代码中定义在include/linux/gfp.h?v=4.7, line 18 ~ line 44, 共计26个掩码信息.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cm">/* Plain integer GFP bitmasks. Do not use this directly. */</span>
<span class="c1">//  区域修饰符
</span><span class="c1"></span><span class="cp">#define ___GFP_DMA              0x01u
</span><span class="cp">#define ___GFP_HIGHMEM          0x02u
</span><span class="cp">#define ___GFP_DMA32            0x04u
</span><span class="cp"></span>
<span class="c1">//  行为修饰符
</span><span class="c1"></span><span class="cp">#define ___GFP_MOVABLE          0x08u       </span><span class="cm">/* 页是可移动的 */</span><span class="cp">
</span><span class="cp">#define ___GFP_RECLAIMABLE      0x10u       </span><span class="cm">/* 页是可回收的 */</span><span class="cp">
</span><span class="cp">#define ___GFP_HIGH             0x20u       </span><span class="cm">/* 应该访问紧急分配池？ */</span><span class="cp">
</span><span class="cp">#define ___GFP_IO               0x40u       </span><span class="cm">/* 可以启动物理IO？ */</span><span class="cp">
</span><span class="cp">#define ___GFP_FS               0x80u       </span><span class="cm">/* 可以调用底层文件系统？ */</span><span class="cp">
</span><span class="cp">#define ___GFP_COLD             0x100u     </span><span class="cm">/* 需要非缓存的冷页 */</span><span class="cp">
</span><span class="cp">#define ___GFP_NOWARN           0x200u     </span><span class="cm">/* 禁止分配失败警告 */</span><span class="cp">
</span><span class="cp">#define ___GFP_REPEAT           0x400u     </span><span class="cm">/* 重试分配，可能失败 */</span><span class="cp">
</span><span class="cp">#define ___GFP_NOFAIL           0x800u     </span><span class="cm">/* 一直重试，不会失败 */</span><span class="cp">
</span><span class="cp">#define ___GFP_NORETRY          0x1000u   </span><span class="cm">/* 不重试，可能失败 */</span><span class="cp">
</span><span class="cp">#define ___GFP_MEMALLOC         0x2000u     </span><span class="cm">/* 使用紧急分配链表 */</span><span class="cp">
</span><span class="cp">#define ___GFP_COMP             0x4000u   </span><span class="cm">/* 增加复合页元数据 */</span><span class="cp">
</span><span class="cp">#define ___GFP_ZERO             0x8000u   </span><span class="cm">/* 成功则返回填充字节0的页 */</span><span class="cp">
</span><span class="cp"></span><span class="c1">//  类型修饰符
</span><span class="c1"></span><span class="cp">#define ___GFP_NOMEMALLOC       0x10000u     </span><span class="cm">/* 不使用紧急分配链表 */</span><span class="cp">
</span><span class="cp">#define ___GFP_HARDWALL         0x20000u     </span><span class="cm">/* 只允许在进程允许运行的CPU所关联的结点分配内存 */</span><span class="cp">
</span><span class="cp">#define ___GFP_THISNODE         0x40000u     </span><span class="cm">/* 没有备用结点，没有策略 */</span><span class="cp">
</span><span class="cp">#define ___GFP_ATOMIC           0x80000u    </span><span class="cm">/* 用于原子分配，在任何情况下都不能中断  */</span><span class="cp">
</span><span class="cp">#define ___GFP_ACCOUNT          0x100000u
</span><span class="cp">#define ___GFP_NOTRACK          0x200000u
</span><span class="cp">#define ___GFP_DIRECT_RECLAIM   0x400000u
</span><span class="cp">#define ___GFP_OTHER_NODE       0x800000u
</span><span class="cp">#define ___GFP_WRITE            0x1000000u
</span><span class="cp">#define ___GFP_KSWAPD_RECLAIM   0x2000000u
</span></code></pre></td></tr></table>
</div>
</div><p><strong>定义掩码</strong></p>
<p>然后第二部分, 相对而言每一个宏又被重新定义如下, 参见include/linux/gfp.h?v=4.7, line 46 ~ line 192</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">  1
</span><span class="lnt">  2
</span><span class="lnt">  3
</span><span class="lnt">  4
</span><span class="lnt">  5
</span><span class="lnt">  6
</span><span class="lnt">  7
</span><span class="lnt">  8
</span><span class="lnt">  9
</span><span class="lnt"> 10
</span><span class="lnt"> 11
</span><span class="lnt"> 12
</span><span class="lnt"> 13
</span><span class="lnt"> 14
</span><span class="lnt"> 15
</span><span class="lnt"> 16
</span><span class="lnt"> 17
</span><span class="lnt"> 18
</span><span class="lnt"> 19
</span><span class="lnt"> 20
</span><span class="lnt"> 21
</span><span class="lnt"> 22
</span><span class="lnt"> 23
</span><span class="lnt"> 24
</span><span class="lnt"> 25
</span><span class="lnt"> 26
</span><span class="lnt"> 27
</span><span class="lnt"> 28
</span><span class="lnt"> 29
</span><span class="lnt"> 30
</span><span class="lnt"> 31
</span><span class="lnt"> 32
</span><span class="lnt"> 33
</span><span class="lnt"> 34
</span><span class="lnt"> 35
</span><span class="lnt"> 36
</span><span class="lnt"> 37
</span><span class="lnt"> 38
</span><span class="lnt"> 39
</span><span class="lnt"> 40
</span><span class="lnt"> 41
</span><span class="lnt"> 42
</span><span class="lnt"> 43
</span><span class="lnt"> 44
</span><span class="lnt"> 45
</span><span class="lnt"> 46
</span><span class="lnt"> 47
</span><span class="lnt"> 48
</span><span class="lnt"> 49
</span><span class="lnt"> 50
</span><span class="lnt"> 51
</span><span class="lnt"> 52
</span><span class="lnt"> 53
</span><span class="lnt"> 54
</span><span class="lnt"> 55
</span><span class="lnt"> 56
</span><span class="lnt"> 57
</span><span class="lnt"> 58
</span><span class="lnt"> 59
</span><span class="lnt"> 60
</span><span class="lnt"> 61
</span><span class="lnt"> 62
</span><span class="lnt"> 63
</span><span class="lnt"> 64
</span><span class="lnt"> 65
</span><span class="lnt"> 66
</span><span class="lnt"> 67
</span><span class="lnt"> 68
</span><span class="lnt"> 69
</span><span class="lnt"> 70
</span><span class="lnt"> 71
</span><span class="lnt"> 72
</span><span class="lnt"> 73
</span><span class="lnt"> 74
</span><span class="lnt"> 75
</span><span class="lnt"> 76
</span><span class="lnt"> 77
</span><span class="lnt"> 78
</span><span class="lnt"> 79
</span><span class="lnt"> 80
</span><span class="lnt"> 81
</span><span class="lnt"> 82
</span><span class="lnt"> 83
</span><span class="lnt"> 84
</span><span class="lnt"> 85
</span><span class="lnt"> 86
</span><span class="lnt"> 87
</span><span class="lnt"> 88
</span><span class="lnt"> 89
</span><span class="lnt"> 90
</span><span class="lnt"> 91
</span><span class="lnt"> 92
</span><span class="lnt"> 93
</span><span class="lnt"> 94
</span><span class="lnt"> 95
</span><span class="lnt"> 96
</span><span class="lnt"> 97
</span><span class="lnt"> 98
</span><span class="lnt"> 99
</span><span class="lnt">100
</span><span class="lnt">101
</span><span class="lnt">102
</span><span class="lnt">103
</span><span class="lnt">104
</span><span class="lnt">105
</span><span class="lnt">106
</span><span class="lnt">107
</span><span class="lnt">108
</span><span class="lnt">109
</span><span class="lnt">110
</span><span class="lnt">111
</span><span class="lnt">112
</span><span class="lnt">113
</span><span class="lnt">114
</span><span class="lnt">115
</span><span class="lnt">116
</span><span class="lnt">117
</span><span class="lnt">118
</span><span class="lnt">119
</span><span class="lnt">120
</span><span class="lnt">121
</span><span class="lnt">122
</span><span class="lnt">123
</span><span class="lnt">124
</span><span class="lnt">125
</span><span class="lnt">126
</span><span class="lnt">127
</span><span class="lnt">128
</span><span class="lnt">129
</span><span class="lnt">130
</span><span class="lnt">131
</span><span class="lnt">132
</span><span class="lnt">133
</span><span class="lnt">134
</span><span class="lnt">135
</span><span class="lnt">136
</span><span class="lnt">137
</span><span class="lnt">138
</span><span class="lnt">139
</span><span class="lnt">140
</span><span class="lnt">141
</span><span class="lnt">142
</span><span class="lnt">143
</span><span class="lnt">144
</span><span class="lnt">145
</span><span class="lnt">146
</span><span class="lnt">147
</span><span class="lnt">148
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cm">/*
</span><span class="cm">* Physical address zone modifiers (see linux/mmzone.h - low four bits)
</span><span class="cm">*
</span><span class="cm">* Do not put any conditional on these. If necessary modify the definitions
</span><span class="cm">* without the underscores and use them consistently. The definitions here may
</span><span class="cm">* be used in bit comparisons.
</span><span class="cm">* 定义区描述符
</span><span class="cm">*/</span>
<span class="cp">#define __GFP_DMA       ((__force gfp_t)___GFP_DMA)
</span><span class="cp">#define __GFP_HIGHMEM   ((__force gfp_t)___GFP_HIGHMEM)
</span><span class="cp">#define __GFP_DMA32     ((__force gfp_t)___GFP_DMA32)
</span><span class="cp">#define __GFP_MOVABLE   ((__force gfp_t)___GFP_MOVABLE)  </span><span class="cm">/* ZONE_MOVABLE allowed */</span><span class="cp">
</span><span class="cp">#define GFP_ZONEMASK    (__GFP_DMA|__GFP_HIGHMEM|__GFP_DMA32|__GFP_MOVABLE)
</span><span class="cp"></span>
<span class="cm">/*
</span><span class="cm">* Page mobility and placement hints
</span><span class="cm">*
</span><span class="cm">* These flags provide hints about how mobile the page is. Pages with similar
</span><span class="cm">* mobility are placed within the same pageblocks to minimise problems due
</span><span class="cm">* to external fragmentation.
</span><span class="cm">*
</span><span class="cm">* __GFP_MOVABLE (also a zone modifier) indicates that the page can be
</span><span class="cm">*   moved by page migration during memory compaction or can be reclaimed.
</span><span class="cm">*
</span><span class="cm">* __GFP_RECLAIMABLE is used for slab allocations that specify
</span><span class="cm">*   SLAB_RECLAIM_ACCOUNT and whose pages can be freed via shrinkers.
</span><span class="cm">*
</span><span class="cm">* __GFP_WRITE indicates the caller intends to dirty the page. Where possible,
</span><span class="cm">*   these pages will be spread between local zones to avoid all the dirty
</span><span class="cm">*   pages being in one zone (fair zone allocation policy).
</span><span class="cm">*
</span><span class="cm">* __GFP_HARDWALL enforces the cpuset memory allocation policy.
</span><span class="cm">*
</span><span class="cm">* __GFP_THISNODE forces the allocation to be satisified from the requested
</span><span class="cm">*   node with no fallbacks or placement policy enforcements.
</span><span class="cm">*
</span><span class="cm">* __GFP_ACCOUNT causes the allocation to be accounted to kmemcg (only relevant
</span><span class="cm">*   to kmem allocations).
</span><span class="cm">*/</span>
<span class="cp">#define __GFP_RECLAIMABLE ((__force gfp_t)___GFP_RECLAIMABLE)
</span><span class="cp">#define __GFP_WRITE     ((__force gfp_t)___GFP_WRITE)
</span><span class="cp">#define __GFP_HARDWALL   ((__force gfp_t)___GFP_HARDWALL)
</span><span class="cp">#define __GFP_THISNODE  ((__force gfp_t)___GFP_THISNODE)
</span><span class="cp">#define __GFP_ACCOUNT   ((__force gfp_t)___GFP_ACCOUNT)
</span><span class="cp"></span>
<span class="cm">/*
</span><span class="cm">* Watermark modifiers -- controls access to emergency reserves
</span><span class="cm">*
</span><span class="cm">* __GFP_HIGH indicates that the caller is high-priority and that granting
</span><span class="cm">*   the request is necessary before the system can make forward progress.
</span><span class="cm">*   For example, creating an IO context to clean pages.
</span><span class="cm">*
</span><span class="cm">* __GFP_ATOMIC indicates that the caller cannot reclaim or sleep and is
</span><span class="cm">*   high priority. Users are typically interrupt handlers. This may be
</span><span class="cm">*   used in conjunction with __GFP_HIGH
</span><span class="cm"> *
</span><span class="cm"> * __GFP_MEMALLOC allows access to all memory. This should only be used when
</span><span class="cm"> *   the caller guarantees the allocation will allow more memory to be freed
</span><span class="cm"> *   very shortly e.g. process exiting or swapping. Users either should
</span><span class="cm"> *   be the MM or co-ordinating closely with the VM (e.g. swap over NFS).
</span><span class="cm"> *
</span><span class="cm"> * __GFP_NOMEMALLOC is used to explicitly forbid access to emergency reserves.
</span><span class="cm"> *   This takes precedence over the __GFP_MEMALLOC flag if both are set.
</span><span class="cm"> */</span>
<span class="cp">#define __GFP_ATOMIC    ((__force gfp_t)___GFP_ATOMIC)
</span><span class="cp">#define __GFP_HIGH      ((__force gfp_t)___GFP_HIGH)
</span><span class="cp">#define __GFP_MEMALLOC  ((__force gfp_t)___GFP_MEMALLOC)
</span><span class="cp">#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC)
</span><span class="cp"></span>
<span class="cm">/*
</span><span class="cm"> * Reclaim modifiers
</span><span class="cm"> *
</span><span class="cm"> * __GFP_IO can start physical IO.
</span><span class="cm"> *
</span><span class="cm"> * __GFP_FS can call down to the low-level FS. Clearing the flag avoids the
</span><span class="cm"> *   allocator recursing into the filesystem which might already be holding
</span><span class="cm"> *   locks.
</span><span class="cm"> *
</span><span class="cm"> * __GFP_DIRECT_RECLAIM indicates that the caller may enter direct reclaim.
</span><span class="cm"> *   This flag can be cleared to avoid unnecessary delays when a fallback
</span><span class="cm"> *   option is available.
</span><span class="cm"> *
</span><span class="cm"> * __GFP_KSWAPD_RECLAIM indicates that the caller wants to wake kswapd when
</span><span class="cm"> *   the low watermark is reached and have it reclaim pages until the high
</span><span class="cm"> *   watermark is reached. A caller may wish to clear this flag when fallback
</span><span class="cm"> *   options are available and the reclaim is likely to disrupt the system. The
</span><span class="cm"> *   canonical example is THP allocation where a fallback is cheap but
</span><span class="cm"> *   reclaim/compaction may cause indirect stalls.
</span><span class="cm"> *
</span><span class="cm"> * __GFP_RECLAIM is shorthand to allow/forbid both direct and kswapd reclaim.
</span><span class="cm"> *
</span><span class="cm"> * __GFP_REPEAT: Try hard to allocate the memory, but the allocation attempt
</span><span class="cm"> *   _might_ fail.  This depends upon the particular VM implementation.
</span><span class="cm"> *
</span><span class="cm"> * __GFP_NOFAIL: The VM implementation _must_ retry infinitely: the caller
</span><span class="cm"> *   cannot handle allocation failures. New users should be evaluated carefully
</span><span class="cm"> *   (and the flag should be used only when there is no reasonable failure
</span><span class="cm"> *   policy) but it is definitely preferable to use the flag rather than
</span><span class="cm"> *   opencode endless loop around allocator.
</span><span class="cm"> *
</span><span class="cm"> * __GFP_NORETRY: The VM implementation must not retry indefinitely and will
</span><span class="cm"> *   return NULL when direct reclaim and memory compaction have failed to allow
</span><span class="cm"> *   the allocation to succeed.  The OOM killer is not called with the current
</span><span class="cm"> *   implementation.
</span><span class="cm"> */</span>
<span class="cp">#define __GFP_IO        ((__force gfp_t)___GFP_IO)
</span><span class="cp">#define __GFP_FS        ((__force gfp_t)___GFP_FS)
</span><span class="cp">#define __GFP_DIRECT_RECLAIM    ((__force gfp_t)___GFP_DIRECT_RECLAIM) </span><span class="cm">/* Caller can reclaim */</span><span class="cp">
</span><span class="cp">#define __GFP_KSWAPD_RECLAIM    ((__force gfp_t)___GFP_KSWAPD_RECLAIM) </span><span class="cm">/* kswapd can wake */</span><span class="cp">
</span><span class="cp">#define __GFP_RECLAIM ((__force gfp_t)(___GFP_DIRECT_RECLAIM|___GFP_KSWAPD_RECLAIM))
</span><span class="cp">#define __GFP_REPEAT    ((__force gfp_t)___GFP_REPEAT)
</span><span class="cp">#define __GFP_NOFAIL    ((__force gfp_t)___GFP_NOFAIL)
</span><span class="cp">#define __GFP_NORETRY   ((__force gfp_t)___GFP_NORETRY)
</span><span class="cp"></span>
<span class="cm">/*
</span><span class="cm"> * Action modifiers
</span><span class="cm"> *
</span><span class="cm"> * __GFP_COLD indicates that the caller does not expect to be used in the near
</span><span class="cm"> *   future. Where possible, a cache-cold page will be returned.
</span><span class="cm"> *
</span><span class="cm"> * __GFP_NOWARN suppresses allocation failure reports.
</span><span class="cm"> *
</span><span class="cm"> * __GFP_COMP address compound page metadata.
</span><span class="cm"> *
</span><span class="cm"> * __GFP_ZERO returns a zeroed page on success.
</span><span class="cm"> *
</span><span class="cm"> * __GFP_NOTRACK avoids tracking with kmemcheck.
</span><span class="cm"> *
</span><span class="cm"> * __GFP_NOTRACK_FALSE_POSITIVE is an alias of __GFP_NOTRACK. It&#39;s a means of
</span><span class="cm"> *   distinguishing in the source between false positives and allocations that
</span><span class="cm"> *   cannot be supported (e.g. page tables).
</span><span class="cm"> *
</span><span class="cm"> * __GFP_OTHER_NODE is for allocations that are on a remote node but that
</span><span class="cm"> *   should not be accounted for as a remote allocation in vmstat. A
</span><span class="cm"> *   typical user would be khugepaged collapsing a huge page on a remote
</span><span class="cm"> *   node.
</span><span class="cm"> */</span>
<span class="cp">#define __GFP_COLD      ((__force gfp_t)___GFP_COLD)
</span><span class="cp">#define __GFP_NOWARN    ((__force gfp_t)___GFP_NOWARN)
</span><span class="cp">#define __GFP_COMP      ((__force gfp_t)___GFP_COMP)
</span><span class="cp">#define __GFP_ZERO      ((__force gfp_t)___GFP_ZERO)
</span><span class="cp">#define __GFP_NOTRACK   ((__force gfp_t)___GFP_NOTRACK)
</span><span class="cp">#define __GFP_NOTRACK_FALSE_POSITIVE (__GFP_NOTRACK)
</span><span class="cp">#define __GFP_OTHER_NODE ((__force gfp_t)___GFP_OTHER_NODE)
</span><span class="cp"></span>
<span class="cm">/* Room for N __GFP_FOO bits */</span>
<span class="cp">#define __GFP_BITS_SHIFT 26
</span><span class="cp">#define __GFP_BITS_MASK ((__force gfp_t)((1 &lt;&lt; __GFP_BITS_SHIFT) - 1))
</span></code></pre></td></tr></table>
</div>
</div><p>给出的常数，其中一些很少使用，因此我不会讨论。其中最重要的一些常数语义如下所示<br />
其中在开始的位置定义了对应的区修饰符, 定义在include/linux/gfp.h?v=4.7, line 46 ~ line 57</p>
<table>
<thead>
<tr>
<th>区修饰符标志</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>__GFP_DMA</td>
<td>从ZONE_DMA中分配内存</td>
</tr>
<tr>
<td>__GFP_HIGHMEM</td>
<td>从ZONE_HIGHMEM活ZONE_NORMAL中分配内存</td>
</tr>
<tr>
<td>__GFP_DMA32</td>
<td>从ZONE_DMA32中分配内存</td>
</tr>
<tr>
<td>__GFP_MOVABLE</td>
<td>从__GFP_MOVABLE中分配内存</td>
</tr>
</tbody>
</table>
<p>其次还定义了我们程序和函数中所需要的掩码MASK的信息, 由于其中__GFP_DMA, __GFP_DMA32, __GFP_HIGHMEM, __GFP_MOVABLE是在内存中分别有对应的内存域信息, 因此我们定义了内存域的掩码GFP_ZONEMASK, 参见include/linux/gfp.h?v=4.7, line 57</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cp">#define GFP_ZONEMASK    (__GFP_DMA|__GFP_HIGHMEM|__GFP_DMA32|__GFP_MOVABLE)
</span></code></pre></td></tr></table>
</div>
</div><p>接着内核定义了行为修饰符</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cm">/* __GFP_WAIT表示分配内存的请求可以中断。也就是说，调度器在该请求期间可随意选择另一个过程执行，或者该请求可以被另一个更重要的事件中断. 分配器还可以在返回内存之前, 在队列上等待一个事件(相关进程会进入睡眠状态).
</span><span class="cm">
</span><span class="cm">虽然名字相似，但__GFP_HIGH与__GFP_HIGHMEM毫无关系，请不要弄混这两者
</span><span class="cm">行为修饰符	描述
</span><span class="cm">__GFP_RECLAIMABLE 
</span><span class="cm">__GFP_MOVABLE	是页迁移机制所需的标志. 顾名思义，它们分别将分配的内存标记为可回收的或可移动的。这影响从空闲列表的哪个子表获取内存
</span><span class="cm">__GFP_WRITE	
</span><span class="cm">__GFP_HARDWALL	只在NUMA系统上有意义. 它限制只在分配到当前进程的各个CPU所关联的结点分配内存。如果进程允许在所有CPU上运行（默认情况），该标志是无意义的。只有进程可以运行的CPU受限时，该标志才有效果
</span><span class="cm">__GFP_THISNODE	也只在NUMA系统上有意义。如果设置该比特位，则内存分配失败的情况下不允许使用其他结点作为备用，需要保证在当前结点或者明确指定的结点上成功分配内存
</span><span class="cm">__GFP_ACCOUNT	
</span><span class="cm">__GFP_ATOMIC	
</span><span class="cm">__GFP_HIGH	如果请求非常重要, 则设置__GFP_HIGH，即内核急切地需要内存时。在分配内存失败可能给内核带来严重后果时(比如威胁到系统稳定性或系统崩溃), 总是会使用该标志
</span><span class="cm">__GFP_MEMALLOC	
</span><span class="cm">__GFP_NOMEMALLOC	
</span><span class="cm">__GFP_IO	说明在查找空闲内存期间内核可以进行I/O操作. 实际上, 这意味着如果内核在内存分配期间换出页, 那么仅当设置该标志时, 才能将选择的页写入硬盘
</span><span class="cm">__GFP_FS	允许内核执行VFS操作. 在与VFS层有联系的内核子系统中必须禁用, 因为这可能引起循环递归调用.
</span><span class="cm">__GFP_DIRECT_RECLAIM	
</span><span class="cm">__GFP_KSWAPD_RECLAIM	
</span><span class="cm">__GFP_RECLAIM	
</span><span class="cm">__GFP_REPEAT	在分配失败后自动重试，但在尝试若干次之后会停止
</span><span class="cm">__GFP_NOFAIL	在分配失败后一直重试，直至成功
</span><span class="cm">__GFP_NORETRY	在分配失败后不重试，因此可能分配失败
</span><span class="cm">__GFP_COLD	如果需要分配不在CPU高速缓存中的“冷”页时，则设置__GFP_COLD
</span><span class="cm">__GFP_NOWARN	在分配失败时禁止内核故障警告。在极少数场合该标志有用
</span><span class="cm">__GFP_COMP	添加混合页元素, 在hugetlb的代码内部使用
</span><span class="cm">__GFP_ZERO	在分配成功时，将返回填充字节0的页
</span><span class="cm">__GFP_NOTRACK	
</span><span class="cm">__GFP_NOTRACK_FALSE_POSITIVE 
</span><span class="cm">__GFP_NOTRACK	
</span><span class="cm">__GFP_OTHER_NODE	
</span></code></pre></td></tr></table>
</div>
</div><p>那自然还有__GFP_BITS_SHIFT来表示我们所有的掩码位, 由于我们共计26个掩码位</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cm">/* Room for N __GFP_FOO bits */</span>
<span class="cp">#define __GFP_BITS_SHIFT 26
</span><span class="cp">#define __GFP_BITS_MASK ((__force gfp_t)((1 &lt;&lt; __GFP_BITS_SHIFT) - 1))
</span></code></pre></td></tr></table>
</div>
</div><p>可以同时指定这些分配标志, 例如</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="n">ptr</span> <span class="o">=</span> <span class="n">kmalloc</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="n">__GFP_IO</span> <span class="o">|</span> <span class="n">__GFP_FS</span><span class="p">);</span>
</code></pre></td></tr></table>
</div>
</div><p>说明页分配器(最终会调用alloc_page)在分配时可以执行I/O, 在必要时还可以执行文件系统操作. 这就让内核有很大的自由度, 以便它尽可能找到空闲的内存来满足分配请求. 大多数分配器都会执行这些修饰符, 但一般不是这样直接指定, 而是将这些行为描述符标志进行分组, 即类型标志</p>
<p><strong>掩码分组</strong></p>
<p>最后来看第三部分, 由于这些标志几乎总是组合使用，内核作了一些分组，包含了用于各种标准情形的适当的标志. 称之为类型标志, 定义在include/linux/gfp.h?v=4.7, lien 194 ~ line 258<br />
类型标志指定所需的行为和区描述符以安城特殊类型的处理, 正因为这一点, 内核总是趋于使用正确的类型标志, 而不是一味地指定它可能用到的多种描述符. 这么做既简单又不容易出错误.<br />
如果有可能的话, 在内存管理子系统之外, 总是把下列分组之一用于内存分配. 在内核源代码中, 双下划线通常用于内部数据和定义. 而这些预定义的分组名没有双下划线前缀, 点从侧面验证了上述说法.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cp">#define GFP_ATOMIC      (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM)
</span><span class="cp">#define GFP_KERNEL      (__GFP_RECLAIM | __GFP_IO | __GFP_FS)
</span><span class="cp">#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | __GFP_ACCOUNT)
</span><span class="cp">#define GFP_NOWAIT      (__GFP_KSWAPD_RECLAIM)
</span><span class="cp">#define GFP_NOIO        (__GFP_RECLAIM)
</span><span class="cp">#define GFP_NOFS        (__GFP_RECLAIM | __GFP_IO)
</span><span class="cp">#define GFP_TEMPORARY   (__GFP_RECLAIM | __GFP_IO | __GFP_FS | \
</span><span class="cp"></span>                         <span class="n">__GFP_RECLAIMABLE</span><span class="p">)</span>
<span class="cp">#define GFP_USER        (__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL)
</span><span class="cp">#define GFP_DMA         __GFP_DMA
</span><span class="cp">#define GFP_DMA32       __GFP_DMA32
</span><span class="cp">#define GFP_HIGHUSER    (GFP_USER | __GFP_HIGHMEM)
</span><span class="cp">#define GFP_HIGHUSER_MOVABLE    (GFP_HIGHUSER | __GFP_MOVABLE)
</span><span class="cp">#define GFP_TRANSHUGE   ((GFP_HIGHUSER_MOVABLE | __GFP_COMP | \
</span><span class="cp"></span>                         <span class="n">__GFP_NOMEMALLOC</span> <span class="o">|</span> <span class="n">__GFP_NORETRY</span> <span class="o">|</span> <span class="n">__GFP_NOWARN</span><span class="p">)</span> <span class="o">&amp;</span> <span class="err">\</span>
                         <span class="o">~</span><span class="n">__GFP_RECLAIM</span><span class="p">)</span>

<span class="cm">/* Convert GFP flags to their corresponding migrate type */</span>
<span class="cp">#define GFP_MOVABLE_MASK (__GFP_RECLAIMABLE|__GFP_MOVABLE)
</span><span class="cp">#define GFP_MOVABLE_SHIFT 3
</span></code></pre></td></tr></table>
</div>
</div><table>
<thead>
<tr>
<th>掩码组</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>GFP_ATOMIC</td>
<td>用于原子分配，在任何情况下都不能中断, 可能使用紧急分配链表中的内存, 这个标志用在中断处理程序, 下半部, 持有自旋锁以及其他不能睡眠的地方</td>
</tr>
<tr>
<td>GFP_KERNEL</td>
<td>这是一种常规的分配方式, 可能会阻塞. 这个标志在睡眠安全时用在进程的长下文代码中. 为了获取调用者所需的内存, 内核会尽力而为. 这个标志应该是首选标志</td>
</tr>
<tr>
<td>GFP_KERNEL_ACCOUNT</td>
<td></td>
</tr>
<tr>
<td>GFP_NOWAIT</td>
<td>与GFP_ATOMIC类似, 不同之处在于, 调用不会退给紧急内存池, 这就增加了内存分配失败的可能性</td>
</tr>
<tr>
<td>GFP_NOIO</td>
<td>这种分配可以阻塞, 但不会启动磁盘I/O, 这个标志在不能引发更多的磁盘I/O时阻塞I/O代码, 这可能导致令人不愉快的递归</td>
</tr>
<tr>
<td>GFP_NOFS</td>
<td>这种分配在必要时可以阻塞, 但是也可能启动磁盘, 但是不会启动文件系统操作, 这个标志在你不鞥在启动另一个文件系统操作时, 用在文件系统部分的代码中</td>
</tr>
<tr>
<td>GFP_TEMPORARY</td>
<td></td>
</tr>
<tr>
<td>GFP_USER</td>
<td>这是一种常规的分配方式, 可能会阻塞. 这个标志用于为用户空间进程分配内存时使用</td>
</tr>
<tr>
<td>GFP_DMA</td>
<td></td>
</tr>
<tr>
<td>GFP_DMA32</td>
<td>用于分配适用于DMA的内存, 当前是__GFP_DMA的同义词, GFP_DMA32也是__GFP_GMA32的同义词</td>
</tr>
<tr>
<td>GFP_HIGHUSER</td>
<td>是GFP_USER的一个扩展, 也用于用户空间. 它允许分配无法直接映射的高端内存. 使用高端内存页是没有坏处的，因为用户过程的地址空间总是通过非线性页表组织的</td>
</tr>
<tr>
<td>GFP_HIGHUSER_MOVABLE</td>
<td>用途类似于GFP_HIGHUSER，但分配将从虚拟内存域ZONE_MOVABLE进行</td>
</tr>
<tr>
<td>GFP_TRANSHUGE</td>
<td></td>
</tr>
</tbody>
</table>
<p>其中GFP_NOIO和GFP_NOFS, 分别明确禁止I/O操作和访问VFS层, 但同时设置了__GFP_RECLAIM，因此可以被回收<br />
而GFP_KERNEL和GFP_USER. 分别是内核和用户分配的默认设置。二者的失败不会立即威胁系统稳定性, GFP_KERNEL绝对是内核源代码中最常使用的标志<br />
最后内核设置了碎片管理的可移动依据组织页的MASK信息GFP_MOVABLE_MASK, 参见include/linux/gfp.h?v=4.7, line 262</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cm">/* Convert GFP flags to their corresponding migrate type */</span>
<span class="cp">#define GFP_MOVABLE_MASK (__GFP_RECLAIMABLE|__GFP_MOVABLE)
</span><span class="cp">#define GFP_MOVABLE_SHIFT 3
</span></code></pre></td></tr></table>
</div>
</div><p>在你编写的绝大多数代码中, 用么用到的是GFP_KERNEL, 要么是GFP_ATOMIC, 当然各个类型标志也均有其应用场景</p>
<table>
<thead>
<tr>
<th>情形</th>
<th>相应标志</th>
</tr>
</thead>
<tbody>
<tr>
<td>进程上下文, 可以睡眠</td>
<td>使用GFP_KERNEL</td>
</tr>
<tr>
<td>进程上下文, 不可以睡眠</td>
<td>使用GFP_KERNEL, 在你睡眠之前或之后以GFP_KERNEL执行内存分配</td>
</tr>
<tr>
<td>中断处理程序</td>
<td>使用GFP_ATMOIC</td>
</tr>
<tr>
<td>软中断</td>
<td>使用GFP_ATMOIC</td>
</tr>
<tr>
<td>tasklet</td>
<td>使用GFP_ATMOIC</td>
</tr>
<tr>
<td>需要用于DMA的内存, 可以睡眠</td>
<td>使用(GFP_DMA GFP_KERNEL)</td>
</tr>
<tr>
<td>需要用于DMA的内存, 不可以睡眠</td>
<td>使用(GFP_DMA GFP_ATOMIC), 或在你睡眠之前执行内存分配</td>
</tr>
</tbody>
</table>
<h3 id="总结">总结</h3>
<p>我们从注释中找到这样的信息, 可以作为参考</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="n">bit</span>       <span class="n">result</span>
<span class="o">=================</span>
<span class="mh">0x0</span>    <span class="o">=&gt;</span> <span class="n">NORMAL</span>
<span class="mh">0x1</span>    <span class="o">=&gt;</span> <span class="n">DMA</span> <span class="n">or</span> <span class="n">NORMAL</span>
<span class="mh">0x2</span>    <span class="o">=&gt;</span> <span class="n">HIGHMEM</span> <span class="n">or</span> <span class="n">NORMAL</span>
<span class="mh">0x3</span>    <span class="o">=&gt;</span> <span class="n">BAD</span> <span class="p">(</span><span class="n">DMA</span><span class="o">+</span><span class="n">HIGHMEM</span><span class="p">)</span>
<span class="mh">0x4</span>    <span class="o">=&gt;</span> <span class="n">DMA32</span> <span class="n">or</span> <span class="n">DMA</span> <span class="n">or</span> <span class="n">NORMAL</span>
<span class="mh">0x5</span>    <span class="o">=&gt;</span> <span class="n">BAD</span> <span class="p">(</span><span class="n">DMA</span><span class="o">+</span><span class="n">DMA32</span><span class="p">)</span>
<span class="mh">0x6</span>    <span class="o">=&gt;</span> <span class="n">BAD</span> <span class="p">(</span><span class="n">HIGHMEM</span><span class="o">+</span><span class="n">DMA32</span><span class="p">)</span>
<span class="mh">0x7</span>    <span class="o">=&gt;</span> <span class="n">BAD</span> <span class="p">(</span><span class="n">HIGHMEM</span><span class="o">+</span><span class="n">DMA32</span><span class="o">+</span><span class="n">DMA</span><span class="p">)</span>
<span class="mh">0x8</span>    <span class="o">=&gt;</span> <span class="n">NORMAL</span> <span class="p">(</span><span class="n">MOVABLE</span><span class="o">+</span><span class="mi">0</span><span class="p">)</span>
<span class="mh">0x9</span>    <span class="o">=&gt;</span> <span class="n">DMA</span> <span class="n">or</span> <span class="n">NORMAL</span> <span class="p">(</span><span class="n">MOVABLE</span><span class="o">+</span><span class="n">DMA</span><span class="p">)</span>
<span class="mh">0xa</span>    <span class="o">=&gt;</span> <span class="n">MOVABLE</span> <span class="p">(</span><span class="n">Movable</span> <span class="n">is</span> <span class="n">valid</span> <span class="n">only</span> <span class="k">if</span> <span class="n">HIGHMEM</span> <span class="n">is</span> <span class="n">set</span> <span class="n">too</span><span class="p">)</span>
<span class="mh">0xb</span>    <span class="o">=&gt;</span> <span class="n">BAD</span> <span class="p">(</span><span class="n">MOVABLE</span><span class="o">+</span><span class="n">HIGHMEM</span><span class="o">+</span><span class="n">DMA</span><span class="p">)</span>
<span class="mh">0xc</span>    <span class="o">=&gt;</span> <span class="n">DMA32</span> <span class="p">(</span><span class="n">MOVABLE</span><span class="o">+</span><span class="n">DMA32</span><span class="p">)</span>
<span class="mh">0xd</span>    <span class="o">=&gt;</span> <span class="n">BAD</span> <span class="p">(</span><span class="n">MOVABLE</span><span class="o">+</span><span class="n">DMA32</span><span class="o">+</span><span class="n">DMA</span><span class="p">)</span>
<span class="mh">0xe</span>    <span class="o">=&gt;</span> <span class="n">BAD</span> <span class="p">(</span><span class="n">MOVABLE</span><span class="o">+</span><span class="n">DMA32</span><span class="o">+</span><span class="n">HIGHMEM</span><span class="p">)</span>
<span class="mh">0xf</span>    <span class="o">=&gt;</span> <span class="n">BAD</span> <span class="p">(</span><span class="n">MOVABLE</span><span class="o">+</span><span class="n">DMA32</span><span class="o">+</span><span class="n">HIGHMEM</span><span class="o">+</span><span class="n">DMA</span><span class="p">)</span>

<span class="n">GFP_ZONES_SHIFT</span> <span class="n">must</span> <span class="n">be</span> <span class="o">&lt;=</span> <span class="mi">2</span> <span class="n">on</span> <span class="mi">32</span> <span class="n">bit</span> <span class="n">platforms</span><span class="p">.</span>

</code></pre></td></tr></table>
</div>
</div><p>很有趣的一点是，没有__GFP_NORMAL常数，而内存分配的主要负担却落到ZONE_NORMAL内存域<br />
内核考虑到这一点, 提供了一个函数gfp_zone来计算与给定分配标志兼容的最高内存域. 那么内存分配可以从该内存域或更低的内存域进行, 该函数定义在include/linux/gfp.h?v=4.7, line 394</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cp">#if defined(CONFIG_ZONE_DEVICE) &amp;&amp; (MAX_NR_ZONES-1) &lt;= 4
</span><span class="cp"></span><span class="cm">/* ZONE_DEVICE is not a valid GFP zone specifier */</span>
<span class="cp">#define GFP_ZONES_SHIFT 2
</span><span class="cp">#else
</span><span class="cp">#define GFP_ZONES_SHIFT ZONES_SHIFT
</span><span class="cp">#endif
</span><span class="cp"></span>
<span class="cp">#if 16 * GFP_ZONES_SHIFT &gt; BITS_PER_LONG
</span><span class="cp">#error GFP_ZONES_SHIFT too large to create GFP_ZONE_TABLE integer
</span><span class="cp">#endif
</span></code></pre></td></tr></table>
</div>
</div><p>由于内存域修饰符的解释方式不是那么直观, 表3-7给出了该函数结果的一个例子, 其中DMA和DMA32内存域相同. 假定在下文中没有设置__GFP_MOVABLE修饰符.</p>
<table>
<thead>
<tr>
<th>修饰符</th>
<th>扫描的内存域</th>
</tr>
</thead>
<tbody>
<tr>
<td>无</td>
<td>ZONE_NORMAL、ZONE_DMA</td>
</tr>
<tr>
<td>__GFP_DMA</td>
<td>ZONE_DMA</td>
</tr>
<tr>
<td>__GFP_DMA &amp; __GFP_HIGHMEM</td>
<td>ZONE_DMA</td>
</tr>
<tr>
<td>__GFP_HIGHMEM</td>
<td>ZONE_HIGHMEM、ZONE_NORMAL、ZONE_DMA</td>
</tr>
</tbody>
</table>
<ul>
<li>如果__GFP_DMA和__GFP_HIGHMEM都没有设置, 则首先扫描ZONE_NORMAL, 后面是ZONE_DMA</li>
<li>如果设置了__GFP_HIGHMEM没有设置__GFP_DMA，则结果是从ZONE_HIGHMEM开始扫描所有3个内存域。</li>
<li>如果设置了__GFP_DMA，那么__GFP_HIGHMEM设置与否没有关系. 只有ZONE_DMA用于3种情形. 这是合理的, 因为同时使用__GFP_HIGHMEM和__GFP_DMA没有意义. 高端内存从来都不适用于DMA<br />
设置__GFP_MOVABLE不会影响内核的决策，除非它与__GFP_HIGHMEM同时指定. 在这种情况下, 会使用特殊的虚拟内存域ZONE_MOVABLE满足内存分配请求. 对前文描述的内核的反碎片策略而言, 这种行为是必要的.<br />
除了内存域修饰符之外, 掩码中还可以设置一些标志.<br />
下图中给出了掩码的布局，以及与各个比特位置关联的常数. __GFP_DMA32出现了几次，因为它可能位于不同的地方<br />
<img src="" alt="" /><br />
与内存域修饰符相反, 这些额外的标志并不限制从哪个物理内存段分配内存, 但确实可以改变分配器的行为. 例如, 它们可以修改查找空闲内存时的积极程度.</li>
</ul>
<h4 id="分配页">分配页</h4>
<h5 id="内存分配统一到alloc_pages接口">内存分配统一到alloc_pages接口</h5>
<p>通过使用标志、内存域修饰符和各个分配函数，内核提供了一种非常灵活的内存分配体系.尽管如此, 所有接口函数都可以追溯到一个简单的基本函数(alloc_pages_node)<br />
分配单页的函数alloc_page和__get_free_page, 还有__get_dma_pages是借助于宏定义的.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="c1">//  http://lxr.free-electrons.com/source/include/linux/gfp.h?v=4.7#L483
</span><span class="c1"></span><span class="cp">#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
</span><span class="cp"></span>
<span class="c1">//  http://lxr.free-electrons.com/source/include/linux/gfp.h?v=4.7#L500
</span><span class="c1"></span><span class="cp">#define __get_free_page(gfp_mask) \
</span><span class="cp"></span>    <span class="n">__get_free_pages</span><span class="p">((</span><span class="n">gfp_mask</span><span class="p">),</span> <span class="mi">0</span><span class="p">)</span><span class="err">`</span>

<span class="c1">//  http://lxr.free-electrons.com/source/include/linux/gfp.h?v=4.7#L503
</span><span class="c1"></span><span class="cp">#define __get_dma_pages(gfp_mask, order) \
</span><span class="cp"></span>    <span class="n">__get_free_pages</span><span class="p">((</span><span class="n">gfp_mask</span><span class="p">)</span> <span class="o">|</span> <span class="n">GFP_DMA</span><span class="p">,</span> <span class="p">(</span><span class="n">order</span><span class="p">))</span>
</code></pre></td></tr></table>
</div>
</div><p>get_zeroed_page的实现也没什么困难, 对__get_free_pages使用__GFP_ZERO标志，即可分配填充字节0的页. 再返回与页关联的内存区地址即可.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="c1">//  http://lxr.free-electrons.com/source/mm/page_alloc.c?v=4.7#L3900
</span><span class="c1"></span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="nf">get_zeroed_page</span><span class="p">(</span><span class="n">gfp_t</span> <span class="n">gfp_mask</span><span class="p">)</span>
<span class="p">{</span>
        <span class="k">return</span> <span class="n">__get_free_pages</span><span class="p">(</span><span class="n">gfp_mask</span> <span class="o">|</span> <span class="n">__GFP_ZERO</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">EXPORT_SYMBOL</span><span class="p">(</span><span class="n">get_zeroed_page</span><span class="p">);</span>
</code></pre></td></tr></table>
</div>
</div><p>__get_free_pages调用alloc_pages完成内存分配, 而alloc_pages又借助于alloc_pages_node<br />
__get_free_pages函数的定义在mm/page_alloc.c?v=4.7, line 3883</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="c1">//  http://lxr.free-electrons.com/source/mm/page_alloc.c?v=4.7#L3883
</span><span class="c1"></span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="nf">__get_free_pages</span><span class="p">(</span><span class="n">gfp_t</span> <span class="n">gfp_mask</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">order</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">struct</span> <span class="nc">page</span> <span class="o">*</span><span class="n">page</span><span class="p">;</span>

    <span class="cm">/*
</span><span class="cm">     * __get_free_pages() returns a 32-bit address, which cannot represent
</span><span class="cm">     * a highmem page
</span><span class="cm">     */</span>
    <span class="n">VM_BUG_ON</span><span class="p">((</span><span class="n">gfp_mask</span> <span class="o">&amp;</span> <span class="n">__GFP_HIGHMEM</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">);</span>

    <span class="n">page</span> <span class="o">=</span> <span class="n">alloc_pages</span><span class="p">(</span><span class="n">gfp_mask</span><span class="p">,</span> <span class="n">order</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">page</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">return</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span><span class="p">)</span> <span class="n">page_address</span><span class="p">(</span><span class="n">page</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">EXPORT_SYMBOL</span><span class="p">(</span><span class="n">__get_free_pages</span><span class="p">);</span>
</code></pre></td></tr></table>
</div>
</div><p>在这种情况下， 使用了一个普通函数而不是宏， 因为alloc_pages返回的page实例需要使用辅助<br />
函数page_address转换为内存地址. 在这里，只要知道该函数可根据page实例计算相关页的线性内存地址即可. 对高端内存页这是有问题的<br />
这样, 就完成了所有分配内存的API函数到公共的基础函数alloc_pages的统一<br />
<img src="" alt="" /></p>
<h5 id="alloc_pages函数分配页">alloc_pages函数分配页</h5>
<p>既然所有的内存分配API函数都可以追溯掉alloc_page函数, 从某种意义上说，该函数是伙伴系统主要实现的”发射台”.<br />
alloc_pages函数的定义是依赖于NUMA或者UMA架构的, 定义如下</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++">
<span class="cp">#ifdef CONFIG_NUMA
</span><span class="cp"></span>
<span class="c1">//  http://lxr.free-electrons.com/source/include/linux/gfp.h?v=4.7#L465
</span><span class="c1"></span><span class="k">static</span> <span class="kr">inline</span> <span class="k">struct</span> <span class="nc">page</span> <span class="o">*</span>
<span class="nf">alloc_pages</span><span class="p">(</span><span class="n">gfp_t</span> <span class="n">gfp_mask</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">order</span><span class="p">)</span>
<span class="p">{</span>
        <span class="k">return</span> <span class="n">alloc_pages_current</span><span class="p">(</span><span class="n">gfp_mask</span><span class="p">,</span> <span class="n">order</span><span class="p">);</span>
<span class="p">}</span>

<span class="cp">#else
</span><span class="cp"></span>
<span class="c1">//  http://lxr.free-electrons.com/source/include/linux/gfp.h?v=4.7#L476
</span><span class="c1"></span><span class="cp">#define alloc_pages(gfp_mask, order) \
</span><span class="cp"></span>                <span class="n">alloc_pages_node</span><span class="p">(</span><span class="n">numa_node_id</span><span class="p">(),</span> <span class="n">gfp_mask</span><span class="p">,</span> <span class="n">order</span><span class="p">)</span>
<span class="cp">#endif
</span></code></pre></td></tr></table>
</div>
</div><p>UMA结构下的alloc_pages是通过alloc_pages_node函数实现的, 下面我们看看alloc_pages_node函数的定义, 在include/linux/gfp.h?v=4.7, line 448</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="c1">//  http://lxr.free-electrons.com/source/include/linux/gfp.h?v=4.7#L448
</span><span class="c1"></span><span class="cm">/*
</span><span class="cm"> * Allocate pages, preferring the node given as nid. When nid == NUMA_NO_NODE,
</span><span class="cm"> * prefer the current CPU&#39;s closest node. Otherwise node must be valid and
</span><span class="cm"> * online.
</span><span class="cm"> */</span>
<span class="k">static</span> <span class="kr">inline</span> <span class="k">struct</span> <span class="nc">page</span> <span class="o">*</span><span class="nf">alloc_pages_node</span><span class="p">(</span><span class="kt">int</span> <span class="n">nid</span><span class="p">,</span> <span class="n">gfp_t</span> <span class="n">gfp_mask</span><span class="p">,</span>
                        <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">order</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">nid</span> <span class="o">==</span> <span class="n">NUMA_NO_NODE</span><span class="p">)</span>
        <span class="n">nid</span> <span class="o">=</span> <span class="n">numa_mem_id</span><span class="p">();</span>

    <span class="k">return</span> <span class="n">__alloc_pages_node</span><span class="p">(</span><span class="n">nid</span><span class="p">,</span> <span class="n">gfp_mask</span><span class="p">,</span> <span class="n">order</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>内核假定传递给改alloc_pages_node函数的结点nid是被激活, 即online的.但是为了安全它还是检查并警告内存结点不存在的情况. 接下来的工作委托给__alloc_pages, 只需传递一组适当的参数, 其中包括节点nid的备用内存域列表zonelist.<br />
现在__alloc_pages函数没什么特别的, 它直接将自己的所有信息传递给__alloc_pages_nodemask来完成内存的分配</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="c1">//  http://lxr.free-electrons.com/source/include/linux/gfp.h?v=4.7#L428
</span><span class="c1"></span><span class="k">static</span> <span class="kr">inline</span> <span class="k">struct</span> <span class="nc">page</span> <span class="o">*</span>
<span class="nf">__alloc_pages</span><span class="p">(</span><span class="n">gfp_t</span> <span class="n">gfp_mask</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">order</span><span class="p">,</span>
        <span class="k">struct</span> <span class="nc">zonelist</span> <span class="o">*</span><span class="n">zonelist</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">return</span> <span class="n">__alloc_pages_nodemask</span><span class="p">(</span><span class="n">gfp_mask</span><span class="p">,</span> <span class="n">order</span><span class="p">,</span> <span class="n">zonelist</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><h5 id="伙伴系统的心脏__alloc_pages_nodemask">伙伴系统的心脏__alloc_pages_nodemask</h5>
<p>内核源代码将__alloc_pages称之为”伙伴系统的心脏”(`the ‘heart’ of the zoned buddy allocator“), 因为它处理的是实质性的内存分配.<br />
由于”心脏”的重要性, 我将在下文详细介绍该函数.<br />
__alloc_pages函数定义在include/linux/gfp.h?v=4.7#L428</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">  1
</span><span class="lnt">  2
</span><span class="lnt">  3
</span><span class="lnt">  4
</span><span class="lnt">  5
</span><span class="lnt">  6
</span><span class="lnt">  7
</span><span class="lnt">  8
</span><span class="lnt">  9
</span><span class="lnt"> 10
</span><span class="lnt"> 11
</span><span class="lnt"> 12
</span><span class="lnt"> 13
</span><span class="lnt"> 14
</span><span class="lnt"> 15
</span><span class="lnt"> 16
</span><span class="lnt"> 17
</span><span class="lnt"> 18
</span><span class="lnt"> 19
</span><span class="lnt"> 20
</span><span class="lnt"> 21
</span><span class="lnt"> 22
</span><span class="lnt"> 23
</span><span class="lnt"> 24
</span><span class="lnt"> 25
</span><span class="lnt"> 26
</span><span class="lnt"> 27
</span><span class="lnt"> 28
</span><span class="lnt"> 29
</span><span class="lnt"> 30
</span><span class="lnt"> 31
</span><span class="lnt"> 32
</span><span class="lnt"> 33
</span><span class="lnt"> 34
</span><span class="lnt"> 35
</span><span class="lnt"> 36
</span><span class="lnt"> 37
</span><span class="lnt"> 38
</span><span class="lnt"> 39
</span><span class="lnt"> 40
</span><span class="lnt"> 41
</span><span class="lnt"> 42
</span><span class="lnt"> 43
</span><span class="lnt"> 44
</span><span class="lnt"> 45
</span><span class="lnt"> 46
</span><span class="lnt"> 47
</span><span class="lnt"> 48
</span><span class="lnt"> 49
</span><span class="lnt"> 50
</span><span class="lnt"> 51
</span><span class="lnt"> 52
</span><span class="lnt"> 53
</span><span class="lnt"> 54
</span><span class="lnt"> 55
</span><span class="lnt"> 56
</span><span class="lnt"> 57
</span><span class="lnt"> 58
</span><span class="lnt"> 59
</span><span class="lnt"> 60
</span><span class="lnt"> 61
</span><span class="lnt"> 62
</span><span class="lnt"> 63
</span><span class="lnt"> 64
</span><span class="lnt"> 65
</span><span class="lnt"> 66
</span><span class="lnt"> 67
</span><span class="lnt"> 68
</span><span class="lnt"> 69
</span><span class="lnt"> 70
</span><span class="lnt"> 71
</span><span class="lnt"> 72
</span><span class="lnt"> 73
</span><span class="lnt"> 74
</span><span class="lnt"> 75
</span><span class="lnt"> 76
</span><span class="lnt"> 77
</span><span class="lnt"> 78
</span><span class="lnt"> 79
</span><span class="lnt"> 80
</span><span class="lnt"> 81
</span><span class="lnt"> 82
</span><span class="lnt"> 83
</span><span class="lnt"> 84
</span><span class="lnt"> 85
</span><span class="lnt"> 86
</span><span class="lnt"> 87
</span><span class="lnt"> 88
</span><span class="lnt"> 89
</span><span class="lnt"> 90
</span><span class="lnt"> 91
</span><span class="lnt"> 92
</span><span class="lnt"> 93
</span><span class="lnt"> 94
</span><span class="lnt"> 95
</span><span class="lnt"> 96
</span><span class="lnt"> 97
</span><span class="lnt"> 98
</span><span class="lnt"> 99
</span><span class="lnt">100
</span><span class="lnt">101
</span><span class="lnt">102
</span><span class="lnt">103
</span><span class="lnt">104
</span><span class="lnt">105
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="c1">//  http://lxr.free-electrons.com/source/mm/page_alloc.c?v=4.7#L3779
</span><span class="c1"></span><span class="cm">/*
</span><span class="cm"> * This is the &#39;heart&#39; of the zoned buddy allocator.
</span><span class="cm"> */</span>
<span class="k">struct</span> <span class="nc">page</span> <span class="o">*</span>
<span class="nf">__alloc_pages_nodemask</span><span class="p">(</span><span class="n">gfp_t</span> <span class="n">gfp_mask</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">order</span><span class="p">,</span>
            <span class="k">struct</span> <span class="nc">zonelist</span> <span class="o">*</span><span class="n">zonelist</span><span class="p">,</span> <span class="n">nodemask_t</span> <span class="o">*</span><span class="n">nodemask</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">struct</span> <span class="nc">page</span> <span class="o">*</span><span class="n">page</span><span class="p">;</span>
    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">cpuset_mems_cookie</span><span class="p">;</span>
    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">alloc_flags</span> <span class="o">=</span> <span class="n">ALLOC_WMARK_LOW</span><span class="o">|</span><span class="n">ALLOC_FAIR</span><span class="p">;</span>
    <span class="n">gfp_t</span> <span class="n">alloc_mask</span> <span class="o">=</span> <span class="n">gfp_mask</span><span class="p">;</span> <span class="cm">/* The gfp_t that was actually used for allocation */</span>
    <span class="k">struct</span> <span class="nc">alloc_context</span> <span class="n">ac</span> <span class="o">=</span> <span class="p">{</span>
        <span class="p">.</span><span class="n">high_zoneidx</span> <span class="o">=</span> <span class="n">gfp_zone</span><span class="p">(</span><span class="n">gfp_mask</span><span class="p">),</span>
        <span class="p">.</span><span class="n">zonelist</span> <span class="o">=</span> <span class="n">zonelist</span><span class="p">,</span>
        <span class="p">.</span><span class="n">nodemask</span> <span class="o">=</span> <span class="n">nodemask</span><span class="p">,</span>
        <span class="p">.</span><span class="n">migratetype</span> <span class="o">=</span> <span class="n">gfpflags_to_migratetype</span><span class="p">(</span><span class="n">gfp_mask</span><span class="p">),</span>
    <span class="p">};</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">cpusets_enabled</span><span class="p">())</span> <span class="p">{</span>
        <span class="n">alloc_mask</span> <span class="o">|=</span> <span class="n">__GFP_HARDWALL</span><span class="p">;</span>
        <span class="n">alloc_flags</span> <span class="o">|=</span> <span class="n">ALLOC_CPUSET</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ac</span><span class="p">.</span><span class="n">nodemask</span><span class="p">)</span>
            <span class="n">ac</span><span class="p">.</span><span class="n">nodemask</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">cpuset_current_mems_allowed</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">gfp_mask</span> <span class="o">&amp;=</span> <span class="n">gfp_allowed_mask</span><span class="p">;</span>

    <span class="n">lockdep_trace_alloc</span><span class="p">(</span><span class="n">gfp_mask</span><span class="p">);</span>

    <span class="n">might_sleep_if</span><span class="p">(</span><span class="n">gfp_mask</span> <span class="o">&amp;</span> <span class="n">__GFP_DIRECT_RECLAIM</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">should_fail_alloc_page</span><span class="p">(</span><span class="n">gfp_mask</span><span class="p">,</span> <span class="n">order</span><span class="p">))</span>
        <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>

    <span class="cm">/*
</span><span class="cm">     * Check the zones suitable for the gfp_mask contain at least one
</span><span class="cm">     * valid zone. It&#39;s possible to have an empty zonelist as a result
</span><span class="cm">     * of __GFP_THISNODE and a memoryless node
</span><span class="cm">     */</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">unlikely</span><span class="p">(</span><span class="o">!</span><span class="n">zonelist</span><span class="o">-&gt;</span><span class="n">_zonerefs</span><span class="o">-&gt;</span><span class="n">zone</span><span class="p">))</span>
        <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">IS_ENABLED</span><span class="p">(</span><span class="n">CONFIG_CMA</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">ac</span><span class="p">.</span><span class="n">migratetype</span> <span class="o">==</span> <span class="n">MIGRATE_MOVABLE</span><span class="p">)</span>
        <span class="n">alloc_flags</span> <span class="o">|=</span> <span class="n">ALLOC_CMA</span><span class="p">;</span>

<span class="nl">retry_cpuset</span><span class="p">:</span>
    <span class="n">cpuset_mems_cookie</span> <span class="o">=</span> <span class="n">read_mems_allowed_begin</span><span class="p">();</span>

    <span class="cm">/* Dirty zone balancing only done in the fast path */</span>
    <span class="n">ac</span><span class="p">.</span><span class="n">spread_dirty_pages</span> <span class="o">=</span> <span class="p">(</span><span class="n">gfp_mask</span> <span class="o">&amp;</span> <span class="n">__GFP_WRITE</span><span class="p">);</span>

    <span class="cm">/*
</span><span class="cm">     * The preferred zone is used for statistics but crucially it is
</span><span class="cm">     * also used as the starting point for the zonelist iterator. It
</span><span class="cm">     * may get reset for allocations that ignore memory policies.
</span><span class="cm">     */</span>
    <span class="n">ac</span><span class="p">.</span><span class="n">preferred_zoneref</span> <span class="o">=</span> <span class="n">first_zones_zonelist</span><span class="p">(</span><span class="n">ac</span><span class="p">.</span><span class="n">zonelist</span><span class="p">,</span>
                    <span class="n">ac</span><span class="p">.</span><span class="n">high_zoneidx</span><span class="p">,</span> <span class="n">ac</span><span class="p">.</span><span class="n">nodemask</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">ac</span><span class="p">.</span><span class="n">preferred_zoneref</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">page</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
        <span class="k">goto</span> <span class="n">no_zone</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="cm">/* First allocation attempt */</span>
    <span class="n">page</span> <span class="o">=</span> <span class="n">get_page_from_freelist</span><span class="p">(</span><span class="n">alloc_mask</span><span class="p">,</span> <span class="n">order</span><span class="p">,</span> <span class="n">alloc_flags</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ac</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">likely</span><span class="p">(</span><span class="n">page</span><span class="p">))</span>
        <span class="k">goto</span> <span class="n">out</span><span class="p">;</span>

    <span class="cm">/*
</span><span class="cm">     * Runtime PM, block IO and its error handling path can deadlock
</span><span class="cm">     * because I/O on the device might not complete.
</span><span class="cm">     */</span>
    <span class="n">alloc_mask</span> <span class="o">=</span> <span class="n">memalloc_noio_flags</span><span class="p">(</span><span class="n">gfp_mask</span><span class="p">);</span>
    <span class="n">ac</span><span class="p">.</span><span class="n">spread_dirty_pages</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>

    <span class="cm">/*
</span><span class="cm">     * Restore the original nodemask if it was potentially replaced with
</span><span class="cm">     * &amp;cpuset_current_mems_allowed to optimize the fast-path attempt.
</span><span class="cm">     */</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">cpusets_enabled</span><span class="p">())</span>
        <span class="n">ac</span><span class="p">.</span><span class="n">nodemask</span> <span class="o">=</span> <span class="n">nodemask</span><span class="p">;</span>
    <span class="n">page</span> <span class="o">=</span> <span class="n">__alloc_pages_slowpath</span><span class="p">(</span><span class="n">alloc_mask</span><span class="p">,</span> <span class="n">order</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ac</span><span class="p">);</span>

<span class="nl">no_zone</span><span class="p">:</span>
    <span class="cm">/*
</span><span class="cm">     * When updating a task&#39;s mems_allowed, it is possible to race with
</span><span class="cm">     * parallel threads in such a way that an allocation can fail while
</span><span class="cm">     * the mask is being updated. If a page allocation is about to fail,
</span><span class="cm">     * check if the cpuset changed during allocation and if so, retry.
</span><span class="cm">     */</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">unlikely</span><span class="p">(</span><span class="o">!</span><span class="n">page</span> <span class="o">&amp;&amp;</span> <span class="n">read_mems_allowed_retry</span><span class="p">(</span><span class="n">cpuset_mems_cookie</span><span class="p">)))</span> <span class="p">{</span>
        <span class="n">alloc_mask</span> <span class="o">=</span> <span class="n">gfp_mask</span><span class="p">;</span>
        <span class="k">goto</span> <span class="n">retry_cpuset</span><span class="p">;</span>
    <span class="p">}</span>

<span class="nl">out</span><span class="p">:</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">kmemcheck_enabled</span> <span class="o">&amp;&amp;</span> <span class="n">page</span><span class="p">)</span>
        <span class="n">kmemcheck_pagealloc_alloc</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">order</span><span class="p">,</span> <span class="n">gfp_mask</span><span class="p">);</span>

    <span class="n">trace_mm_page_alloc</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">order</span><span class="p">,</span> <span class="n">alloc_mask</span><span class="p">,</span> <span class="n">ac</span><span class="p">.</span><span class="n">migratetype</span><span class="p">);</span>

    <span class="k">return</span> <span class="n">page</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">EXPORT_SYMBOL</span><span class="p">(</span><span class="n">__alloc_pages_nodemask</span><span class="p">);</span>
</code></pre></td></tr></table>
</div>
</div><h5 id="__free_pages">__free_pages</h5>
<p>类似地，内存释放函数也可以归约到一个主要的函数(__free_pages), 只是用不同的参数调用而已<br />
前面我们讲过内核释放的两个主要函数有__free_page和free_page, 它们的定义在include/linux/gfp.h?v=4.7#L519</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-c++" data-lang="c++"><span class="kt">void</span> <span class="nf">free_pages</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">addr</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">order</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">addr</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">VM_BUG_ON</span><span class="p">(</span><span class="o">!</span><span class="n">virt_addr_valid</span><span class="p">((</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">addr</span><span class="p">));</span>
        <span class="n">__free_pages</span><span class="p">(</span><span class="n">virt_to_page</span><span class="p">((</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">addr</span><span class="p">),</span> <span class="n">order</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>free_pages和__free_pages之间的关系通过函数而不是宏建立, 因为首先必须将虚拟地址转换为指向struct page的指针<br />
virt_to_page将虚拟内存地址转换为指向page实例的指针. 基本上, 这是讲解内存分配函数时介绍的page_address辅助函数的逆过程.<br />
下图以图形化方式综述了各个内存释放函数之间的关系</p>
<p><img src="" alt="" /></p>

    </article>
    
    <script>
  'use strict';
  
  function wrap(el, wrapper) {
    el.parentNode.insertBefore(wrapper, el);
    wrapper.appendChild(el);
  }

  (function () {
    var singleContentsElem = document.querySelector('.single__contents');
    singleContentsElem ? 
    singleContentsElem.querySelectorAll('pre > code').forEach(function(elem) {
      var dataLang = elem.getAttribute('data-lang');
      var dataLangWrapper = document.createElement('div');
      var code = null;
      var codeTitle = null;

      if (dataLang && dataLang.includes(':')) {
        code = dataLang.split(':')[0];
        codeTitle = dataLang.split(':')[1];

        dataLangWrapper.className = 'language-' + code;
        dataLangWrapper.setAttribute('data-lang', codeTitle);

        elem.className = 'language-' + code;
        elem.setAttribute('data-lang', codeTitle);
        elem.setAttribute('id', codeTitle);
      } else if (!dataLang) {
        dataLangWrapper.setAttribute('data-lang', 'Code');
        dataLangWrapper.className = 'language-code';
      }

      if (!dataLang || codeTitle) {
        wrap(elem.parentNode, dataLangWrapper);
      }

    }) : null;
  })();

  var langCodeElem = document.querySelectorAll('.language-code');
  langCodeElem ? langCodeElem.forEach(function (elem) {
    var newElem = document.createElement('span');
    newElem.className = 'copy-to-clipboard';
    newElem.setAttribute('title', 'Copy to clipboard');
    elem.append(newElem);
  }) : null;
  
</script>
    
    
    
<div class="whoami__gutter"></div>
<hr class="hr-slash whoami-hr"/>
<section class="whoami">
  <div class="whoami__image-wrapper">
    
    
      
        <img data-src="/images/whoami/avatar.jpg" src="data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath fill='%23aaa' d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 16H6c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h12c.55 0 1 .45 1 1v12c0 .55-.45 1-1 1zm-4.44-6.19l-2.35 3.02-1.56-1.88c-.2-.25-.58-.24-.78.01l-1.74 2.23c-.26.33-.02.81.39.81h8.98c.41 0 .65-.47.4-.8l-2.55-3.39c-.19-.26-.59-.26-.79 0z'/%3E%3C/svg%3E" alt="月冰升" class="lazyload whoami__image"/>
      
    
  </div>
  <div class="whoami__contents">
    <div class="whoami__written-by">
      作者
    </div>
    <div class="whoami__title">
      
        月冰升
      
    </div>
    <div class="whoami__desc">
      
        Shaddy
      
    </div>
    <div class="whoami__social">
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    </div>
  </div>
</section>
<hr class="hr-slash whoami-hr" />


    <section class="related">
    
    
    <h1 class="related__title">
      <hr class="hr-dots"/>
      <div>
        相关内容
      </div>
      <hr class="hr-dots"/>
    </h1>
    <ul class="related-ul">
        
        <li>
          <a href="/zh/linux/linuxcore/notes/linux%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86&#43;%E7%BA%BF%E6%80%A7%E5%9C%B0%E5%9D%80&#43;%E9%9D%9E%E8%BF%9E%E7%BB%AD%E5%8C%BA%E5%9F%9F/" class="related__link">linux内存管理&#43;线性地址&#43;非连续区域</a>
        </li>
        
        <li>
          <a href="/zh/linux/linuxcore/notes/linux%E5%86%85%E6%A0%B8%E4%BB%A3%E7%A0%81%E6%B3%A8%E8%AE%B0/" class="related__link">linux内核代码注记</a>
        </li>
        
        <li>
          <a href="/zh/linux/linuxcore/notes/meminfo-%E7%AA%A5%E6%8E%A2/" class="related__link">meminfo 窥探</a>
        </li>
        
        <li>
          <a href="/zh/linux/linuxcore/notes/openstack%E5%B0%8F%E5%9E%8B%E7%B3%BB%E7%BB%9F%E6%90%AD%E5%BB%BA/" class="related__link">openstack</a>
        </li>
        
        <li>
          <a href="/zh/linux/linuxcore/notes/slab/" class="related__link">slab</a>
        </li>
        
    </ul>
    
  </section>
    <div class="grow"></div>
<nav class="pagination-single">
  
    
      <a href="http://shaddy.gitee.io/zh/linux/linuxcore/notes/%E5%86%85%E5%AD%98%E5%AF%BB%E5%9D%80%E5%AF%84%E5%AD%98%E5%99%A8%E7%AC%94%E8%AE%B0/" class="pagination-single__left">
        <div class="pagination-single__icon">
          <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M19 11H7.83l4.88-4.88c.39-.39.39-1.03 0-1.42-.39-.39-1.02-.39-1.41 0l-6.59 6.59c-.39.39-.39 1.02 0 1.41l6.59 6.59c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L7.83 13H19c.55 0 1-.45 1-1s-.45-1-1-1z"/></svg>
        </div>
        <div class="pagination-single__left-title">内存寻址寄存器笔记</div>      
      </a>
    
    <div class="grow"></div>
    
      <a href="http://shaddy.gitee.io/zh/linux/linuxcore/notes/linux%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86&#43;%E7%BA%BF%E6%80%A7%E5%9C%B0%E5%9D%80&#43;%E9%9D%9E%E8%BF%9E%E7%BB%AD%E5%8C%BA%E5%9F%9F/" class="pagination-single__right">      
        <div class="pagination-single__right-title">linux内存管理&#43;线性地址&#43;非连续区域</div>
        <div class="pagination-single__icon">
          <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M5 13h11.17l-4.88 4.88c-.39.39-.39 1.03 0 1.42.39.39 1.02.39 1.41 0l6.59-6.59c.39-.39.39-1.02 0-1.41l-6.58-6.6c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L16.17 11H5c-.55 0-1 .45-1 1s.45 1 1 1z"/></svg>
        </div>
      </a>
    
  
</nav>
    
  

    <div class="modal micromodal-slide" id="modal" aria-hidden="true">
  <div class="modal__overlay" tabindex="-1" data-micromodal-close>
    <div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-title">
      
      <div class="modal__content" id="modal-content">
        <div id="mySwipe" class="swipe">
          <div class="swipe-wrap">
          </div>
        </div>
      </div>

      <span class="modal__items">
        
        <span class="modal__header">
          <div class="modal__paging" title="Page Info" aria-label="Current Page">
          </div>
          <div class="modal__icon modal__toolbar modal__toolbar--close" title="Close" aria-label="Close Button" data-micromodal-close>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26" width="25" height="25"><path fill="currentColor" d="M 21.734375 19.640625 L 19.636719 21.734375 C 19.253906 22.121094 18.628906 22.121094 18.242188 21.734375 L 13 16.496094 L 7.761719 21.734375 C 7.375 22.121094 6.746094 22.121094 6.363281 21.734375 L 4.265625 19.640625 C 3.878906 19.253906 3.878906 18.628906 4.265625 18.242188 L 9.503906 13 L 4.265625 7.761719 C 3.882813 7.371094 3.882813 6.742188 4.265625 6.363281 L 6.363281 4.265625 C 6.746094 3.878906 7.375 3.878906 7.761719 4.265625 L 13 9.507813 L 18.242188 4.265625 C 18.628906 3.878906 19.257813 3.878906 19.636719 4.265625 L 21.734375 6.359375 C 22.121094 6.746094 22.121094 7.375 21.738281 7.761719 L 16.496094 13 L 21.734375 18.242188 C 22.121094 18.628906 22.121094 19.253906 21.734375 19.640625 Z"/></svg>
          </div>
          <div class="modal__icon modal__toolbar modal__toolbar--full" title="Full Screen" aria-label="Full Screen Button">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="25" height="25"><path fill="currentColor" d="M 5 3 C 3.9069372 3 3 3.9069372 3 5 L 3 8 A 1.0001 1.0001 0 1 0 5 8 L 5 5 L 8 5 A 1.0001 1.0001 0 1 0 8 3 L 5 3 z M 16 3 A 1.0001 1.0001 0 1 0 16 5 L 19 5 L 19 8 A 1.0001 1.0001 0 1 0 21 8 L 21 5 C 21 3.9069372 20.093063 3 19 3 L 16 3 z M 3.984375 14.986328 A 1.0001 1.0001 0 0 0 3 16 L 3 19 C 3 20.093063 3.9069372 21 5 21 L 8 21 A 1.0001 1.0001 0 1 0 8 19 L 5 19 L 5 16 A 1.0001 1.0001 0 0 0 3.984375 14.986328 z M 19.984375 14.986328 A 1.0001 1.0001 0 0 0 19 16 L 19 19 L 16 19 A 1.0001 1.0001 0 1 0 16 21 L 19 21 C 20.093063 21 21 20.093063 21 19 L 21 16 A 1.0001 1.0001 0 0 0 19.984375 14.986328 z"/></svg>
          </div>
          <div class="modal__icon modal__toolbar modal__toolbar--normal" title="Normal Screen" aria-label="Normal Screen Button">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="25" height="25"><path fill="currentColor" d="M 16.96875 4.972656 C 15.867188 4.988281 14.984375 5.894531 15 7 L 15 15 L 7 15 C 6.277344 14.988281 5.609375 15.367188 5.246094 15.992188 C 4.878906 16.613281 4.878906 17.386719 5.246094 18.007813 C 5.609375 18.632813 6.277344 19.011719 7 19 L 19 19 L 19 7 C 19.007813 6.460938 18.796875 5.941406 18.414063 5.558594 C 18.03125 5.175781 17.511719 4.964844 16.96875 4.972656 Z M 32.96875 4.972656 C 31.921875 4.988281 31.0625 5.8125 31.003906 6.859375 C 31 6.90625 31 6.953125 31 7 L 31 19 L 43 19 C 43.066406 19 43.132813 19 43.199219 18.992188 C 44.269531 18.894531 45.070313 17.972656 45.015625 16.902344 C 44.964844 15.828125 44.074219 14.988281 43 15 L 35 15 L 35 7 C 35.007813 6.460938 34.796875 5.941406 34.414063 5.558594 C 34.03125 5.175781 33.511719 4.964844 32.96875 4.972656 Z M 7 31 C 6.277344 30.988281 5.609375 31.367188 5.246094 31.992188 C 4.878906 32.613281 4.878906 33.386719 5.246094 34.007813 C 5.609375 34.632813 6.277344 35.011719 7 35 L 15 35 L 15 43 C 14.988281 43.722656 15.367188 44.390625 15.992188 44.753906 C 16.613281 45.121094 17.386719 45.121094 18.007813 44.753906 C 18.632813 44.390625 19.011719 43.722656 19 43 L 19 31 Z M 31 31 L 31 43 C 30.988281 43.722656 31.367188 44.390625 31.992188 44.753906 C 32.613281 45.121094 33.386719 45.121094 34.007813 44.753906 C 34.632813 44.390625 35.011719 43.722656 35 43 L 35 35 L 43 35 C 43.722656 35.011719 44.390625 34.632813 44.753906 34.007813 C 45.121094 33.386719 45.121094 32.613281 44.753906 31.992188 C 44.390625 31.367188 43.722656 30.988281 43 31 Z"/></svg>
          </div>
        </span>
        
        <div class="modal__icon modal__arrow modal__arrow--left" title="Arrow Left" aria-label="Arrow Left Button">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26" width="28" height="28"><path fill="currentColor" d="M 23.28125 11 L 10 10 L 10 6.851563 C 10 6.523438 9.839844 6.277344 9.519531 6.03125 C 9.199219 5.949219 8.878906 5.949219 8.640625 6.113281 C 5.359375 8.410156 2.238281 12.257813 2.160156 12.421875 C 2.082031 12.578125 2.007813 12.8125 2.003906 12.976563 C 2.003906 12.980469 2 12.988281 2 12.992188 C 2 13.15625 2.078125 13.402344 2.160156 13.484375 C 2.238281 13.648438 5.28125 17.507813 8.640625 19.804688 C 8.960938 19.96875 9.28125 20.050781 9.519531 19.886719 C 9.839844 19.722656 10 19.476563 10 19.148438 L 10 16 L 23.28125 15 C 23.679688 14.679688 24 13.875 24 12.992188 C 24 12.195313 23.761719 11.320313 23.28125 11 Z"/></svg>
        </div>
        
        <div class="modal__icon modal__arrow modal__arrow--right" title="Arrow Right" aria-label="Arrow Right Button">

          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26" width="28" height="28"><path fill="currentColor" d="M 2.71875 11.023438 L 16 10.023438 L 16 6.875 C 16 6.546875 16.160156 6.300781 16.480469 6.054688 C 16.800781 5.972656 17.121094 5.972656 17.359375 6.136719 C 20.640625 8.433594 23.761719 12.28125 23.839844 12.445313 C 23.917969 12.601563 23.992188 12.835938 23.996094 13 C 23.996094 13.003906 24 13.011719 24 13.015625 C 24 13.179688 23.921875 13.425781 23.839844 13.507813 C 23.761719 13.671875 20.71875 17.53125 17.359375 19.828125 C 17.039063 19.992188 16.71875 20.074219 16.480469 19.910156 C 16.160156 19.746094 16 19.5 16 19.171875 L 16 16.023438 L 2.71875 15.023438 C 2.320313 14.703125 2 13.898438 2 13.015625 C 2 12.21875 2.238281 11.34375 2.71875 11.023438 Z"/></svg>
        </div>

        <div class="modal__caption">
          <div class="modal__caption--text">
          </div>
        </div>

      </span>
    </div>
  </div>
</div>


<script defer src="/js/swipe.min.08ff2be1b74347bad9e267f732f1388d271b2f9d97b6a8a87bd6a510f17c5ad2.js"></script>

<script defer src="/js/micromodal.min.de01b44b2f383056bbcaf6ee921fd385d79108ec1129afd0eb2f3f5a07e11f45.js"></script>

<script defer src="/js/helper/fadeinout.min.js"></script>

<script>
document.addEventListener('DOMContentLoaded', function () {
  
   
  var docElem = document.documentElement;

   
  function openFullscreen() {
    if (docElem.requestFullscreen) {
      docElem.requestFullscreen();
    } else if (docElem.mozRequestFullScreen) {  
      docElem.mozRequestFullScreen();
    } else if (docElem.webkitRequestFullscreen) {  
      docElem.webkitRequestFullscreen();
    } else if (docElem.msRequestFullscreen) {  
      docElem.msRequestFullscreen();
    }
  }

   
  function closeFullscreen() {
    if (document.fullscreenElement ||
      document.webkitFullscreenElement ||
      document.mozFullScreenElement) {
      if (document.exitFullscreen) {
        document.exitFullscreen();
      } else if (document.mozCancelFullScreen) {  
        document.mozCancelFullScreen();
      } else if (document.webkitExitFullscreen) {  
        document.webkitExitFullscreen();
      } else if (document.msExitFullscreen) {  
        document.msExitFullscreen();
      }
    }
  }

  var modal = document.getElementById('modal');
  var galleryContainerElem = document.querySelector('.gallery__container');
  var swipeWrapElem = document.querySelector('.swipe-wrap');
  var mySwipeElem = document.getElementById('mySwipe');
  var arrowLeftElem = document.querySelector('.modal__arrow--left');
  var arrowRightElem = document.querySelector('.modal__arrow--right');
  var closeElem = document.querySelector('.modal__toolbar--close');
  var fullElem = document.querySelector('.modal__toolbar--full');
  var normalElem = document.querySelector('.modal__toolbar--normal');
  var captionElem = document.querySelector('.modal__caption');
  var pagingElem = document.querySelector('.modal__paging');
  var itemsElem = document.querySelector('.modal__items');
  var imgTotalNum = null;
  var myFadeTimeout = null;
  var mySwipe = null;
  var keydownFunction = function (e) {
    if (e.key === 'ArrowRight') {
      if (modal && modal.classList.contains('is-open')) {
        mySwipe.next();
      }
    } else if (e.key === 'ArrowLeft') {
      if (modal && modal.classList.contains('is-open')) {
        mySwipe.prev();
      }
    }
  }

  if (galleryContainerElem) {
    imgTotalNum = galleryContainerElem.querySelectorAll('img').length;
  } else {
    galleryContainerElem = document.querySelector('.single__contents');
    imgTotalNum = galleryContainerElem.querySelectorAll('img').length;
  }

  MicroModal.init({
    onClose: function () {
      if (mySwipe) {
        mySwipe.kill();
        mySwipe = null;
        closeFullscreen();
      }
      window.removeEventListener('keydown', keydownFunction);
    },
    disableScroll: true,
    disableFocus: true,
    awaitOpenAnimation: false,
    awaitCloseAnimation: false,
    debugMode: false,
  });

  var imageLoad = function(src) {
    return new Promise(function(resolve, reject) {
      var newImg = new Image;
      newImg.onload = function() {
        resolve(newImg);
      }
      newImg.onerror = reject;
      newImg.src = src;
    });
  }

  galleryContainerElem.querySelectorAll('img').forEach(function (elem, idx) {
    elem.style.cursor = 'pointer';

    var clonedElem = elem.cloneNode(true);
    clonedElem.style.maxHeight = '100%';
    clonedElem.style.maxWidth = '100%';
    clonedElem.onclick = function (e) {
      e.stopPropagation();
    }

    var wrapper = document.createElement('div');
    wrapper.style.width = '100%';
    wrapper.style.height = '100vh';
    wrapper.setAttribute('data-micromodal-close', '');
    wrapper.onclick = function () {
      if (mySwipe) {
        mySwipe.kill();
        mySwipe = null;
      }
    }
    wrapper.onmouseenter = function () {
      clearTimeout(myFadeTimeout);
      fadeIn(itemsElem, 200);
    };
    wrapper.onmouseleave = function () {
      myFadeTimeout = setTimeout(function () {
        fadeOut(itemsElem, 200);
      }, 2500);
    }
    wrapper.ontouchstart = function() {
      fadeIn(itemsElem, 200);
    }
    wrapper.append(clonedElem);
    swipeWrapElem.append(wrapper);

    elem.addEventListener('click', async function (e) {
      MicroModal.show('modal');
      if (mySwipe) {
        mySwipe.kill();
        mySwipe = null;
      }

      var imgSrc = e.target.getAttribute('data-src') || e.target.getAttribute('src');
      var img = await imageLoad(imgSrc);
      clonedElem.style.width = img.width + 'px';
      clonedElem.style.height = img.height + 'px';
      
      
      mySwipe = new Swipe(mySwipeElem, {
        startSlide: idx,
        draggable: true,
        autoRestart: false,
        continuous: false,
        disableScroll: true,
        stopPropagation: true,
        callback: async function (index, element) {
          
          var imgElem = element.querySelector('img');
          var imgSrc = imgElem.getAttribute('data-src') || imgElem.getAttribute('src');
          var img = await imageLoad(imgSrc);
          imgElem.style.width = img.width + 'px';
          imgElem.style.height = img.height + 'px';

          
          if (captionElem && imgElem) {
            var caption = null;
            if (imgElem.getAttribute('data-caption')) {
              caption = imgElem.getAttribute('data-caption');
            } else if (imgElem.getAttribute('title')) {
              caption = imgElem.getAttribute('title');
            } else if (imgElem.getAttribute('alt')) {
              caption = imgElem.getAttribute('alt');
            } else {
              caption = imgElem.getAttribute('src');
            }

            captionElem.querySelector('.modal__caption--text').innerText = caption;
            pagingElem.innerText = (index + 1) + ' / ' + imgTotalNum;

            clearTimeout(myFadeTimeout);
            fadeIn(itemsElem, 200);
          }
        },
      });

      fadeIn(itemsElem);

      
      if (captionElem) {
        var caption = null;
        if (e.target.getAttribute('data-caption')) {
          caption = e.target.getAttribute('data-caption');
        } else if (e.target.getAttribute('title')) {
          caption = e.target.getAttribute('title');
        } else if (e.target.getAttribute('alt')) {
          caption = e.target.getAttribute('alt');
        } else {
          caption = e.target.getAttribute('src');
        }

        captionElem.querySelector('.modal__caption--text').innerText = caption;
        pagingElem.innerText = (idx + 1) + ' / ' + imgTotalNum;
      }

      if (normalElem && fullElem) {
        normalElem.style.zIndex = -1;
        normalElem.style.opacity = 0;
        fullElem.style.zIndex = 25;
        fullElem.style.opacity = 1;
      }
    });

    window.addEventListener('keydown', keydownFunction);
  });

  arrowLeftElem ?
    arrowLeftElem.addEventListener('click', function (e) {
      if (mySwipe) {
        mySwipe.prev();
      }
    }) : null;
  arrowRightElem ?
    arrowRightElem.addEventListener('click', function (e) {
      if (mySwipe) {
        mySwipe.next();
      }
    }) : null;

  closeElem ?
    closeElem.addEventListener('click', function () {
      if (mySwipe) {
        mySwipe.kill();
        mySwipe = null;
      }
      closeFullscreen();
      MicroModal.close('modal');
    }) : null;

  fullElem ?
    fullElem.addEventListener('click', function (e) {
      openFullscreen();
      if (normalElem) {
        normalElem.style.zIndex = 25;
        normalElem.style.opacity = 1;
        fullElem.style.zIndex = -1;
        fullElem.style.opacity = 0;
      }
    }) : null;

  normalElem ?
    normalElem.addEventListener('click', function (e) {
      closeFullscreen();
      if (fullElem) {
        fullElem.style.zIndex = 25;
        fullElem.style.opacity = 1;
        normalElem.style.zIndex = -1;
        normalElem.style.opacity = 0;
      }
    }) : null;
  
});
</script>

    <div class="hide">
      

<div class="search">
  <span class="icon">
    <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" fill="currentColor" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M15.5 14h-.79l-.28-.27c1.2-1.4 1.82-3.31 1.48-5.34-.47-2.78-2.79-5-5.59-5.34-4.23-.52-7.79 3.04-7.27 7.27.34 2.8 2.56 5.12 5.34 5.59 2.03.34 3.94-.28 5.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
  </span>
  <input id="search" aria-label="Site Search" class="input" type="text" placeholder="搜索" autocomplete="off">
  <div id="search-results" class="dropdown">
    <div id="search-menu" class="dropdown-menu" role="menu">
    </div>
  </div>
</div>


    </div>
  </div>
</main>


<aside class="single__side main-side">
  


<section class="sidebar hide">
  <script>document.querySelector('.sidebar').classList.remove('hide')</script>
  <div class="toc__flexbox" data-position="fixed">
    <h6 class="toc__title" data-ani="true">目录</h6>
    
      <label class="switch" data-ani="true">
        <input id="toggle-toc" aria-label="Toggle TOC" type="checkbox" checked>
        <span class="slider round"></span>
      </label>
    
  </div>
  <div class="toc " data-dir="ltr" data-folding="false" data-ani="true">
    <nav id="TableOfContents">
  <ul>
    <li>
      <ul>
        <li><a href="#伙伴系统数据结构">伙伴系统数据结构</a></li>
        <li><a href="#内存区是如何连接的">内存区是如何连接的</a></li>
        <li><a href="#避免碎片">避免碎片</a></li>
        <li><a href="#分配器api">分配器API</a></li>
        <li><a href="#总结">总结</a></li>
      </ul>
    </li>
  </ul>
</nav>
  </div>
</section>



</aside>

<script>
  
  
  

  var enableToc = JSON.parse("true");
  var toc = JSON.parse("null");
  var tocPosition = JSON.parse("\"inner\"");
  
  var singleMainElem = document.querySelector('.single__main');
  var singleSideElem = document.querySelector('.single__side');

  enquire.register("screen and (max-width: 769px)", {
    match: function () {
      if ((enableToc || toc) && tocPosition !== "outer") {
        if (singleMainElem && singleSideElem) {
          singleMainElem.classList.remove('main-main');
          singleMainElem.classList.add('main');
          singleSideElem.classList.remove('main-side');
          singleSideElem.classList.add('hide');
        }
      } else if (tocPosition === "outer") {
        if (singleMainElem && !singleMainElem.classList.contains('main-main')) {
          singleMainElem.classList.remove('main-main');
          singleMainElem.classList.add('main');
        }
        if (singleSideElem && !singleSideElem.classList.contains('hide')) {
          singleSideElem.classList.add('hide');
        }
      }
    },
    unmatch: function () {
      if ((enableToc || toc) && tocPosition !== "outer") {
        singleMainElem.classList.remove('main');
        singleMainElem.classList.add('main-main');
        singleSideElem.classList.remove('hide');
        singleSideElem.classList.add('main-side');
      } else if (tocPosition === "outer") {
        if (singleMainElem && !singleMainElem.classList.contains('main-main')) {
          singleMainElem.classList.remove('main-main');
          singleMainElem.classList.add('main');
        }
        if (singleSideElem && !singleSideElem.classList.contains('hide')) {
          singleSideElem.classList.add('hide');
        }
      }

      var navCollapseBtn = document.querySelector('.navbar__burger');
      var navCollapse = document.getElementsByClassName('navbarm__collapse')[0];
      if (navCollapse) {
        navCollapse.setAttribute('data-open', false);
        navCollapse.style.maxHeight = 0;
        navCollapseBtn.classList.remove('is-active');
      }
      document.getElementsByClassName('navbar__menu')[0].classList.remove('is-active');
      document.getElementsByClassName('mobile-search')[0].classList.add('hide');
    },
    setup: function () { },
    deferSetup: true,
    destroy: function () { },
  });
</script>




<script defer src="/js/clipboard.min.1626706afc88d95ebe1173b553ec732c6dc82a576989315fdf5e7779af738a44.js"></script>

<script defer src="/js/helper/getParents.min.js"></script>

<script defer src="/js/helper/closest.min.js"></script>

<script defer src="/js/helper/prev.min.js"></script>

<script defer src="/js/helper/prop.min.js"></script>

<script defer src="/js/helper/fadeinout.min.js"></script>

















<script>
  'use strict';

  window.onload = function() {
    var navbar = document.querySelector('.navbar');
    var singleContentsElem = document.querySelector('.single__contents');

    
    
    
    var enableBusuanzi = JSON.parse("false");
    var busuanziPagePV = JSON.parse("true");
    
    if (enableBusuanzi && busuanziPagePV) {
      var pagePvElem = document.querySelector('#busuanzi_value_page_pv');
      pagePvElem.textContent = pagePvElem.textContent.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
    }    
    


    
    
    
    
    
    
    var enableToc = JSON.parse("true");
    var toc = JSON.parse("null");
    var hideToc = JSON.parse("false");
    var tocFlexbox = document.querySelector('.toc__flexbox');
    var tocFlexboxOuter = document.querySelector('.toc__flexbox--outer');
    var tocFolding = JSON.parse("null");
    
    if ((enableToc || toc) && document.querySelector('.toc')) {
      var tableOfContentsElem = document.querySelector('.toc').querySelector('#TableOfContents');

      if (false === tocFolding) {

      } else {
        tableOfContentsElem.querySelectorAll('ul') ?
          tableOfContentsElem.querySelectorAll('ul').forEach(function (rootUl) {
            rootUl.querySelectorAll('li').forEach(function (liElem) {
              liElem.querySelectorAll('ul').forEach(function (ulElem) {
                ulElem.style.display = 'none';
              });
            });
          }) : null;
      }

      if (tableOfContentsElem) {
        if (tableOfContentsElem.querySelectorAll('a').length > 0) {
          tableOfContentsElem.querySelectorAll('a').forEach(function (elem) {
            elem.addEventListener('click', function () {
              var id = elem.getAttribute('id');
              navbar.classList.remove('navbar--show');
              navbar.classList.remove('navbar--hide');
              navbar.classList.add('navbar--hide');

              document.querySelector('.toc').querySelectorAll('a').forEach(function (elem) {
                elem.classList.remove('active');
              });
              elem.classList.add('active');

              var curElem = tableOfContentsElem.querySelector('[href="#' + id + '"]');
              if (curElem && curElem.nextElementSibling) {
                curElem.nextElementSibling.style.display = 'block';
              }
              if (curElem) {
                getParents(curElem, 'ul') ?
                  getParents(curElem, 'ul').forEach(function (elem) {
                    elem.style.display = 'block';
                  }) : null;
              }
            });
          });
        } else {
          if (tocFlexbox) {
            tocFlexbox.setAttribute('data-position', '');
            if (!tocFlexbox.classList.contains('hide')) {
              tocFlexbox.classList.add('hide');
            }
          }
          if (tocFlexboxOuter) {
            tocFlexboxOuter.setAttribute('data-position', '');
            if (!tocFlexboxOuter.classList.contains('hide')) {
              tocFlexboxOuter.classList.add('hide');
            }
          }
        }
      }

      
      var toggleTocElem = document.getElementById("toggle-toc");
      var visibleTocElem = document.getElementById('visible-toc');
      var tocElem = document.querySelector('.toc');
      var mainElem = document.querySelector('main');
      var sideElem = document.querySelector('side');
      var tocFlexboxElem = document.querySelector('.toc__flexbox');

      toggleTocElem ? 
      toggleTocElem.addEventListener('change', function(e) {
        if (e.target.checked) {
          if (tocElem) {
            fadeIn(tocElem, 200);
          }
          if (tocFlexboxElem) {
            tocFlexboxElem.setAttribute('data-position', 'fixed');
          }

          if (mainElem) {
            mainElem.classList.remove('main-main');
            mainElem.classList.remove('main');
            mainElem.classList.add('main-main');
          }
          if (sideElem) {
            sideElem.classList.remove('main-side');
          }
        } else {
          if (tocElem) {
            fadeOut(tocElem, 200);
          }
          if (tocFlexboxElem) {
            tocFlexboxElem.setAttribute('data-position', 'absolute');
          }

          if (mainElem) {
            mainElem.classList.remove('main-main');
            mainElem.classList.remove('main');
            mainElem.classList.add('main');
          }
          if (sideElem) {
            sideElem.classList.remove('main-side');
          }
        }
      }) : null;

      visibleTocElem ?
      visibleTocElem.addEventListener('change', function(e) {
        if (e.target.checked) {
          if (tocElem) {
            fadeIn(tocElem, 200);
          }
        } else {
          if (tocElem) {
            fadeOut(tocElem, 200);
          }
        }
      }) : null;
    }
    


    
    var tables = document.querySelectorAll('.single__contents > table');
    for (let i = 0; i < tables.length; i++) {
      var table = tables[i];
      var wrapper = document.createElement('div');
      wrapper.className = 'table-wrapper';
      table.parentElement.replaceChild(wrapper, table);
      wrapper.appendChild(table);
    }
    


    
    var text, clip = new ClipboardJS('.anchor');
    var headers = singleContentsElem.querySelectorAll("h1, h2, h3, h4");

    
    var languagedir = JSON.parse("\"ltr\"");

    headers ? 
    headers.forEach(function (elem) {
      var url = encodeURI(document.location.origin + document.location.pathname);
      var link = url + "#" + elem.getAttribute('id');
      var newElemOuter = document.createElement('span');
      newElemOuter.classList.add('anchor');
      newElemOuter.classList.add('hide');
      newElemOuter.setAttribute('data-clipboard-text', link);
      newElemOuter.style.position = 'relative';

      var newElemInner = document.createElement('span');
      newElemInner.style.fontSize = '1rem';
      newElemInner.style.position = 'absolute';
      newElemInner.style.top = '50%';
      newElemInner.style.transform = 'translateY(-50%)';
      newElemInner.innerText = "🔗";

      if (languagedir === "rtl") {
        newElemInner.style.left = '-2rem';
      } else {
        newElemInner.style.right = '-2rem';
      }

      newElemOuter.append(newElemInner);
      elem.append(newElemOuter);

      elem.addEventListener('mouseenter', function() {
        this.querySelector('.anchor').classList.remove('hide');
      });
      elem.addEventListener('mouseleave', function () {
        this.querySelector('.anchor').classList.add('hide');
      });
    }) : null;

    document.querySelectorAll('.anchor').forEach(function(elem) {
      elem.addEventListener('mouseleave', function() {
        elem.setAttribute('aria-label', null);
        elem.classList.remove('tooltipped');
        elem.classList.remove('tooltipped-s');
        elem.classList.remove('tooltipped-w');
      });
    });

    clip.on('success', function (e) {
      e.clearSelection();
      e.trigger.setAttribute('aria-label', 'Link copied to clipboard!');
      e.trigger.classList.add('tooltipped');
      e.trigger.classList.add('tooltipped-s');
    });
    


    
    var clipInit = false;
    var preChromaElem = document.querySelectorAll('pre.chroma');
    var langCodeElem = document.querySelectorAll('.language-code');
    var dollarCodeElem = document.querySelectorAll('div.language-\\$');
    var gtCodeElem = document.querySelectorAll('div.language-\\>');

    var makeClipboard = function(elem) {
      var code = elem,
        text = elem.textContent;
        
      if (text.length > 15) {
        if (!clipInit) {
          var text, clip = new ClipboardJS('.copy-to-clipboard', {
            text: function (trigger) {
              var codeElem = prev(trigger).querySelectorAll('code');
              if (codeElem.length > 1) {
                text = prev(trigger).querySelector('code[class^="language-"]').textContent;
              } else {
                text = prev(trigger).querySelector('code').textContent;
              }

              return text.replace(/^\$\s/gm, '');
            }
          });

          var inPre;
          clip.on('success', function (e) {
            e.clearSelection();
            inPre = prop(e.trigger.parentNode, 'tagName') == 'PRE';
            e.trigger.setAttribute('aria-label', 'Copied to clipboard!');
            e.trigger.classList.add('tooltipped');
            e.trigger.classList.add('tooltipped-w');
          });

          clip.on('error', function (e) {
            inPre = prop(e.trigger.parentNode, 'tagName') == 'PRE';
            e.trigger.setAttribute('aria-label', e.action.toString());
            e.trigger.classList.add('tooltipped');
            e.trigger.classList.add('tooltipped-w');
          });

          clipInit = true;
        }

        var notAllowedClass = ['language-mermaid', 'language-viz', 'language-wave', 'language-chart', 'language-msc', 'language-flowchart'];
        var isNotAllowedIncluded = false;
        var curClassName = code.getAttribute('class');

        for (var i = 0; i < notAllowedClass.length; i++) {
          if (curClassName && curClassName.startsWith(notAllowedClass[i])) {
            isNotAllowedIncluded = true;
            break;
          }
        }

        if (!isNotAllowedIncluded) {
          if (curClassName) {
            var newClipboardElem = document.createElement('span');
            newClipboardElem.setAttribute('class', 'copy-to-clipboard');
            newClipboardElem.setAttribute('title', 'Copy to clipboard');
            elem.parentNode.parentNode.insertBefore(newClipboardElem, elem.parentNode.nextElementSibling);
          }
        }
      }
    }

    var makeSymbolClipboard = function(elem) {
      var clipboardSpan = document.createElement('span');
      clipboardSpan.setAttribute('class', 'copy-to-clipboard');
      clipboardSpan.setAttribute('title', 'Copy to clipboard');
      elem.parentNode.parentNode.insertBefore(clipboardSpan, elem.parentNode.nextElementSibling);
    }

    preChromaElem ? 
    preChromaElem.forEach(function(elem) {
      elem.querySelectorAll('code').forEach(function(codeElem) {
        makeClipboard(codeElem);
      });
    }) : null;
    
    langCodeElem ? 
    langCodeElem.forEach(function(elem) {
      elem.querySelectorAll('code').forEach(function (codeElem) {
        makeClipboard(codeElem);
      });
    }) : null;

    dollarCodeElem ? 
    dollarCodeElem.forEach(function(elem) {
      elem.querySelectorAll('code').forEach(function (codeElem) {
        makeSymbolClipboard(codeElem);
      });
    }) : null;

    gtCodeElem ?
    gtCodeElem.forEach(function(elem) {
      elem.querySelectorAll('code').forEach(function (codeElem) {
        makeSymbolClipboard(codeElem);
      });
    }) : null;
    


    
    dollarCodeElem ?
    dollarCodeElem.forEach(function(elem) {
      var lnts = elem.parentNode.parentNode ? elem.parentNode.parentNode.querySelectorAll('.lnt') : null;
      lnts ? 
      lnts.forEach(function(lnt) {
        lnt.innerHTML = '$<br/>';
      }) : null;
    }) : null;

    gtCodeElem ?
    gtCodeElem.forEach(function(elem) {
      var lnts = elem.parentNode.parentNode ? elem.parentNode.parentNode.querySelectorAll('.lnt') : null;
      lnts ? 
      lnts.forEach(function(lnt) {
        lnt.innerHTML = '><br/>';
      }) : null;
    }) : null;
    


    
    
    var lib = JSON.parse("null");

    if (lib && lib.includes('mermaid')) {
      
      var themeVariant = localStorage.getItem('theme') || JSON.parse("\"dark\"");

      if (themeVariant === "dark" || themeVariant === "hacker") {
        mermaid.initialize({ theme: 'dark' });
      } else {
        mermaid.initialize({ theme: 'default' });
      }
      
      var mermaids = [];
      [].push.apply(mermaids, document.getElementsByClassName('language-mermaid'));
      mermaids.forEach(function(elem) {
        var elemParentNode = elem.parentNode;

        if (elemParentNode !== document.body) {
          elemParentNode.parentNode.insertBefore(elem, elemParentNode);
          elemParentNode.parentNode.removeChild(elemParentNode);
        }

        var newElemWrapper = document.createElement('div');
        newElemWrapper.classList.add('mermaid');
        newElemWrapper.style.padding = '34px 4px 6px';
        newElemWrapper.innerHTML = elem.innerHTML;
        elem.replaceWith(newElemWrapper);
      });
    }
    
    

    
    if (lib && lib.includes('katex')) {
      var mathElements = document.getElementsByClassName('math');
      var options = {
        delimiters: [
          { left: "$$", right: "$$", display: true },
          { left: "\\[", right: "\\]", display: true },
          { left: "$", right: "$", display: false },
          { left: "\\(", right: "\\)", display: false }
        ],
      };

      renderMathInElement(document.querySelector('.single__contents'), options);
    }
    


    
    if (lib && lib.includes('flowchartjs')) {
      
      var options = JSON.parse("{\"arrow-end\":\"block\",\"element-color\":\"black\",\"fill\":\"white\",\"flowstate\":{\"approved\":{\"fill\":\"#58C4A3\",\"font-size\":12,\"no-text\":\"n/a\",\"yes-text\":\"APPROVED\"},\"current\":{\"fill\":\"yellow\",\"font-color\":\"red\",\"font-weight\":\"bold\"},\"future\":{\"fill\":\"#FFFF99\"},\"invalid\":{\"fill\":\"#444444\"},\"past\":{\"fill\":\"#CCCCCC\",\"font-size\":12},\"rejected\":{\"fill\":\"#C45879\",\"font-size\":12,\"no-text\":\"REJECTED\",\"yes-text\":\"n/a\"},\"request\":{\"fill\":\"blue\"}},\"font-color\":\"black\",\"font-size\":14,\"line-color\":\"black\",\"line-length\":50,\"line-width\":3,\"no-text\":\"no\",\"scale\":1,\"symbols\":{\"end\":{\"class\":\"end-element\"},\"start\":{\"element-color\":\"green\",\"fill\":\"yellow\",\"font-color\":\"red\"}},\"text-margin\":10,\"x\":0,\"y\":0,\"yes-text\":\"yes\"}");
      var jsonContent = null;

      var flowchartPrefix = "language-flowchart";
      var index = 0;
      Array.prototype.forEach.call(document.querySelectorAll("[class^=" + flowchartPrefix + "]"), function(x){
          x.style.display = 'none'
          x.parentNode.style.backgroundColor = "transparent"
          jsonContent = x.innerText;

          var node0 = document.createElement('div');
          node0.id = 'flowchart' + index;
          x.parentNode.insertBefore(node0, x);

          var diagram = flowchart.parse(jsonContent);
          diagram.drawSVG("flowchart"+index, options);

          index +=1;
      });      
    }
    


    
    


    
    if (lib && lib.includes('msc')) {
      
      var options = JSON.parse("{\"theme\":\"hand\"}");
      var jsonContent = null;

      var index = 0;
      var chartPrefix = "language-msc";
      Array.prototype.forEach.call(document.querySelectorAll("[class^=" + chartPrefix + "]"), function (x) {
        x.style.display = 'none'
        x.parentNode.style.backgroundColor = "transparent"
        jsonContent = x.innerText;
        var node0 = document.createElement('div');
        node0.id = 'msc' + index;
        x.parentNode.insertBefore(node0, x);
        var diagram = Diagram.parse(jsonContent);
        diagram.drawSVG("msc" + index, options);
        index += 1;
      });
    }
    


    
    if (lib && lib.includes('chart')) {
      var borderColor = "#666";
      var bgColor = "#ddd";
      var borderWidth = 2;

      Chart.defaults.global.elements.rectangle.borderWidth = borderWidth;
      Chart.defaults.global.elements.rectangle.borderColor = borderColor;
      Chart.defaults.global.elements.rectangle.backgroundColor = bgColor;

      Chart.defaults.global.elements.line.borderWidth = borderWidth;
      Chart.defaults.global.elements.line.borderColor = borderColor;
      Chart.defaults.global.elements.line.backgroundColor = bgColor;

      Chart.defaults.global.elements.point.borderWidth = borderWidth;
      Chart.defaults.global.elements.point.borderColor = borderColor;
      Chart.defaults.global.elements.point.backgroundColor = bgColor;

      var chartPrefix = "language-chart";
      var index = 0;
      var jsonContent = null;

      Array.prototype.forEach.call(document.querySelectorAll("[class^=" + chartPrefix + "]"), function (x) {
        x.style.display = 'none'
        x.parentNode.style.backgroundColor = "transparent"
        jsonContent = x.innerText;
        var node0 = document.createElement('canvas');
        var source = null;
        node0.height = 200;
        node0.style.height = 200;
        node0.id = 'myChart' + index;
        source = JSON.parse(jsonContent);
        x.parentNode.insertBefore(node0, x);
        var ctx = document.getElementById('myChart' + index).getContext('2d');
        var myChart = new Chart(ctx, source);
        index += 1;
      });            
    }
    


    
    if (lib && lib.includes('wavedrom')) {
      var wavePrefix = "language-wave";
      var index = 0;
      var jsonContent = null;
      
      Array.prototype.forEach.call(document.querySelectorAll("[class^=" + wavePrefix + "]"), function (x) {
        x.style.display = 'none'
        x.parentNode.style.backgroundColor = "transparent"
        jsonContent = x.innerText;
        var node0 = document.createElement('div');
        var source = null;
        node0.id = 'WaveDrom_Display_' + index;
        source = JSON.parse(jsonContent);
        x.parentNode.insertBefore(node0, x);
        WaveDrom.RenderWaveForm(index, source, "WaveDrom_Display_");
        index += 1;
      });
    }
    


    
    if (lib && lib.includes('viz')) {
      var vizPrefix = "language-viz-";
      Array.prototype.forEach.call(document.querySelectorAll("[class^=" + vizPrefix + "]"), function (x) {
        x.style.display = 'none'
        x.parentNode.style.backgroundColor = "transparent"
        var engine;
        x.getAttribute("class").split(" ").forEach(function (cls) {
          if (cls.startsWith(vizPrefix)) {
            engine = cls.substr(vizPrefix.length);
          }
        });
        var viz = new Viz();
        viz.renderSVGElement(x.innerText, { engine: engine })
          .then(function (element) {
            element.style.width = "100%";
            x.parentNode.insertBefore(element, x);
          })
      });
    }
    
    
  }
</script>


            
            <footer class="footer">
    
<div class="dropdown">
  <button class="dropdown-trigger" aria-label="Select Theme Button">
    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M12.65 15.67c.14-.36.05-.77-.23-1.05l-2.09-2.06.03-.03c1.74-1.94 2.98-4.17 3.71-6.53h1.94c.54 0 .99-.45.99-.99v-.02c0-.54-.45-.99-.99-.99H10V3c0-.55-.45-1-1-1s-1 .45-1 1v1H1.99c-.54 0-.99.45-.99.99 0 .55.45.99.99.99h10.18C11.5 7.92 10.44 9.75 9 11.35c-.81-.89-1.49-1.86-2.06-2.88-.16-.29-.45-.47-.78-.47-.69 0-1.13.75-.79 1.35.63 1.13 1.4 2.21 2.3 3.21L3.3 16.87c-.4.39-.4 1.03 0 1.42.39.39 1.02.39 1.42 0L9 14l2.02 2.02c.51.51 1.38.32 1.63-.35zM17.5 10c-.6 0-1.14.37-1.35.94l-3.67 9.8c-.24.61.22 1.26.87 1.26.39 0 .74-.24.88-.61l.89-2.39h4.75l.9 2.39c.14.36.49.61.88.61.65 0 1.11-.65.88-1.26l-3.67-9.8c-.22-.57-.76-.94-1.36-.94zm-1.62 7l1.62-4.33L19.12 17h-3.24z"/></svg>
  </button>
  <div class="dropdown-content">
    
    
    
      
      
        
      
        
      
        
        
          
            
              <a href="http://shaddy.gitee.io/zh/linux/linuxcore/notes/linux%E5%86%85%E5%AD%98%E7%89%A9%E7%90%86%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E4%BC%99%E4%BC%B4%E7%B3%BB%E7%BB%9F/" data-lang="zh" class="dropdown-item is-active">中文</a>
            
          
        
      
    
  </div>
</div>

    
<div class="footer__social">
  <div class="social">
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
            
    
    
  
  
    
      <a href="http://shaddy.gitee.io/zh/linux//index.xml" type="application/rss+xml" title="RSS" aria-label="RSS Feed Link">
        <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><circle fill="currentColor" cx="6.18" cy="17.82" r="2.18"/><path fill="currentColor" d="M5.59 10.23c-.84-.14-1.59.55-1.59 1.4 0 .71.53 1.28 1.23 1.4 2.92.51 5.22 2.82 5.74 5.74.12.7.69 1.23 1.4 1.23.85 0 1.54-.75 1.41-1.59-.68-4.2-3.99-7.51-8.19-8.18zm-.03-5.71C4.73 4.43 4 5.1 4 5.93c0 .73.55 1.33 1.27 1.4 6.01.6 10.79 5.38 11.39 11.39.07.73.67 1.28 1.4 1.28.84 0 1.5-.73 1.42-1.56-.73-7.34-6.57-13.19-13.92-13.92z"/></svg>
      </a>
    
  

  </div>
</div>

    
<div id="gtt">
  <div class="gtt">
    <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M8.12 14.71L12 10.83l3.88 3.88c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L12.7 8.71c-.39-.39-1.02-.39-1.41 0L6.7 13.3c-.39.39-.39 1.02 0 1.41.39.38 1.03.39 1.42 0z"/></svg>
  </div>
</div>

    <hr />

    <div class="basicflex">
        
    </div>

    <div class="footer__poweredby">
        
                
            <p class="caption">
                
                    ©2020, All Rights Reserved
                
            </p>
        

        
            <p class="caption">Powered by <a href="https://gohugo.io/" target="_blank" rel="noreferrer">Hugo</a> and the <a href="https://github.com/zzossig/hugo-theme-zzo" target="_blank" rel="noreferrer">Zzo theme</a></p>
        
        
    </div> 
</footer>
        </div>
        





<div class="wrapper__right hide" data-pad="true" dir="ltr">
  <script>document.querySelector('.wrapper__right').classList.remove('hide')</script>
  
</div>

    </div>
</body>

</html>